Sunday 6 March 2016

ASP.NET and the JS 2k16 toolchain (or how I learned to stop worrying and love the Bower)

Here is a blog post detailing my journey of discovery over 1 weekend into the new (at least to ASP.NET developers) JS tools that are being adopted by Visual Studio and are making their way into ASP.NET developers lives.

Note, this article contains changes of opinion, a word count that some viewers may find offencive, and beer. You have been warned.

Introduction

Those of you who’ve worked with me in a development context might have heard me saying recently that the way JS is going as an ASP.NET developer can be a little overwhelming. Some of it may boil down to this: The “old” way seemed easy. Here’s how it seemed before.

Step 1: Install packages using NuGet.
Step 2: If using Typescript (and you should be), get the definitions files, probably from NuGet. Sure, they might be a little hard to find, but they’re probably there, somewhere.
Step 3: Set up your BundleConfig.cs to bundle them up.
Step 4: Use them!

Sure, it’s not perfect. But it was workable. Until it wasn’t.

But the future, as it’s known to do, came. And now people are grunting, gulping, bowing and doing all sorts of other verbs to do with JS. As an outsider, someone who gasp uses Microsoft technology like some sort of heathen, the future’s all great, but really has never been explained satisfactorily. Well, alright, that’s not fair. It’s been explained, but never end to end. There’s always bits missing, and contradictions, and with the speed of developments on the interwebz, by the time you come to read an article or watch a course online, it’s starting to go (or is already) out of date, and you could wind up trying to work with out of date information.

Here’s how I naively saw JS development as of now (early 2016). Note, this will probably already be out of date.

Step 1: Get node. Which, secretly gives you NPM (a package manager). 1
Step 2: Use NPM (that package manager) to get bower, a package manager
Step 3: Use bower (that 2nd package manager) to get JSPM, another package manager.
Step 4: Get your javascript packages from 1 of the 3 package managers. Some may exist on several. Work out which goes where.
Step 5: Compile stuff using a gulp file, which uses gulp, which does something to do with pipes and plumbing and tiny Italian plumbers. *
Step 6: Do stuff

* There may or may not be tiny Italian plumbers involved in gulp.js. I can neither confirm or deny.

Like it or not, this is the direction things seemed to be heading. I’m sure it made sense to someone, somewhere. But I’d never get the hang of it. Or, at least, that’s how I used to think.

ASP.NET Core

There is, of course, ASP.NET Core, promising to revolutionise the framework. Included in the changes are a big reshuffle of how we do client side files in ASP.NET, bringing it inline with how people without powerful IDEs handle front end development. I imagined it to be more like my 2nd list above, but for reasons I’ll explain later I’m more hopeful now.

For a bit of extra reading on ASP.NET Core, I recommend this blog post:

I think it’s important to mention ASP.NET Core here, as it reflects again the direction JS development seems to be going for ASP.NET. However, I won’t be using ASP.NET core in this post, I’ll be using ASP.NET 4.6 for a few reasons. Firstly, there’s posts aplenty of people using ASP.NET core with this toolchain, whilst comparitively few of people using ASP.NET 4.6. I was surprised and happy to hear this could be done without moving frameworks (after all, ASP.NET Core is more than just a rebranding of ASP.NET, see Dusted Codes post for more info). Secondly, and possibly more importantly, ASP.NET core is still just an RC. Which means it’s probably fine for prototyping, playing with and learning, but there may be trouble ahead if you stray too far off the path, and not a lot of documentation to help when you do. A lot of that is paraphrasing from my workplace’s JS guru, who I hope I’ve not misquoted.

The ASPNET JS 2k16 Toolchain

This is not a term that exists, nor do I suspect it will catch on. But it’s my way of referring to the modern (as of early 2016) toolchain for building complex JS apps, particularly as someone moving at JS from a Visual Studio oriented perspective as I am. As someone attacking this from a Php or Ruby background, I might choose differently.

So, what I mean by the ASPNET JS 2k16 toolchain is (or are):

  • Typescript
  • NodeJS and NPM (spoiler: They’re interrelated)
  • Gulp
  • Bower

I know bower can do more than mere JavaScript (it can handle CSS packages like Bootstrap too). But they seem like solid candidates for a good ASPNET friendly JS toolchain. All supported by VS 2015 out of the box and all things that JavaScript and front end developers have already been using for a long time.

If you feel like I’ve missed an essential tool for 2016 JS development with ASP.NET, feel free to let me know in the comments. But I think these are definitely the top 4. Especially after my experience of using them all in conjunction that I picked up writing this post.

Don’t knock it ‘til you try it

I had a need to write an SPA, or at least wanted to play with the concept. I’ve used Backbone.js before, with the aforementioned NuGet package way of getting it into your solution. But I’ve meant to play with Angular for a while now. 2

A previous attempt saw me try to do it using NuGet packages and trying to get the typescript typings for it, but unsuccessfully for some reason or another (sorry, it was a long time ago). So I decided to try Angular again today. I know this post isn’t necessarily about Angular, but it was my inspiration.

The old way didn’t work

So, great, we’re going to try using Angular. I’ve got my ASP.NET 4.6 empty project sat there, ready for some good stuff. Now what?

I first thought I’d try grabbing hold of a quickstart project template from NuGet, to kickstart my coding. However, though there were many templates covering JQuery, Angular with Mvc or Web API, I couldn’t find any that had them with Typescript in them. There may be some, but I couldn’t find them.

Next, I tried NuGet, as any typical back end dev might. And guess what, there’s angularjs! And angularjs.core. And angularjs.route. And where are those damned typescript definition files? 3

A little confusing, I think you’ll agree. I could’ve persisted, but I thought I’d see if it was something I was doing wrong. This was bound to be an already solved problem, right? So off I go to Google, looking for anything that might help me link up the technologies I wanted to use (angular, Typescript and Visual Studio). Lots of articles had 2 out of 3 sorted, but the first one I found that had me covered was this blog post by Christos S. I recommend giving it a read if you want to give this a try. I didn’t follow his instructions strictly, but it helped show me that:

  • You don’t need to use ASP.NET Core to use the VS built in support for Javascript tooling such as Bower (I already knew it supported gulp files). And the tooling is good!
  • The Russian dolls of package managers aren’t so confusing as you’d think!

Worth noting that he only uses npm and bower, which seem to be the 2 most popular. There’s JSPM too, but I haven’t moved onto that yet. There are various others too, all with different amounts of traction.

So, I thought, now’s as good a time as any. It doesn’t seem as bad as I worried it might be, and doing it the NuGety way would probably take roughly as long. So, let’s give it a try!

A good way for us to proceed might be to use his blog post to help us get set up. This section of the post will follow me following along this blog post, detailing my own discoveries along the way. I hope that’s not too confusing. If it helps, keep his post open in a separate tab. I will have all the information here too, but you can see where I deviate from his pattern of doing things for both expediency and just generally to fit my style better.

Getting the tools in place

Firstly, we’ll need to get your dev environment set up. All the steps here only have to be performed once per machine, ever (hopefully).

Get Node

Nice and easy to start, and you might already have it. I’m on a new computer here, so I didn’t. Go to the NodeJS download page and grab node for your system. Install it as normal.

This not only gives you node for using Node.JS, but also comes bundled with NPM, which you’ll need for this tutorial.

Get Bower

We’re going to use a package manager to get a package manager. But this will be the only time in this post we’ll do it. … I think.

Now you’ve got npm installed, it’s on your path in your command line. Launch a command line session, and we’re going to use npm to install bower.

npm install -g bower

Note the “-g” in that command. It means global. It just effects where it stores the module it’s retrieved. As we’re probably going to want bower and all these tools for more than 1 project, we’ll get them globally.

Off it’ll go, and soon enough you’ll have bower! It too, will now be on your path (you may have to relaunch your cmd or powershell session for it to be on the path). To check if it’s there, try:

bower -v

It’ll spit out your version number to the console. Not so bad, right?

Get gulp

Gulp is like MSBuild for client side things (CSS, Javascript, Typescript, less, etc). It allows us to compile, clean, minify and do all that kind of pre-deployment stuff.

Note, this used to be handled in ASP.NET by bundling and the BundleConfig.cs. See this blog post on Fritz on the Web for more information on why moving to these newer technologies is a good idea. It focuses particularly on ASP.NET core, but as you’ll discover, you can use them earlier if you’d like.

This step’s easy now you know how.

npm install -g gulp

Voila!

Worth noting, you may get a few warnings about dependencies of gulp being out of date, etc. I’m not sure of the implications, but it hasn’t effected how it works for me. So you’ll probably be fine.

Get TypeScript

I love TypeScript. I think it’s a great way of writing large JavaScript applications. The guys behind Angular 2 obviously agree, Angular 2 is written in TypeScript, after all.

When you install Visual Studio 2015, TypeScript comes bundled in by default. But it’s also worth installing it as a node package. You never know.

npm install -g typescript

Get Typings

For working with external libraries, TypeScript has the concept of definitions files (think of them as the .xml files XMLDoc puts out for your projects in .NET, combined with allowing type safety and intellisense).

In Chris S’s blog post, he recommends installing tsd to retrieve the various definition files. But we’re not going to do that (spoiler: Installing it spits out a warning that it is depricated and you should use Typings).

Let’s install Typings, a nice command line node package that allows us to grab d.ts files for all manner of JS libraries and frameworks.

npm install -g typings

Getting to some code

It’s (finally) showtime! Fire up VS and get a new project going. You’ll want a blank Web app (Empty template), with MVC and Web API.

Get the basics ready

Before we get into anything fancy, let’s get some basics for an SPA ready.

Firstly, create a new MVC controller in the Controllers folder (you knew that, right?). Just call it home, or application, or something like that. This controller only needs one action for now, Index, which will just return a view. This view will contain our SPA logic. Simple, right?

From here, make an index view (you can right click inside the controller action and select Add New View as a shortcut). You probably won’t want it having a layout.

It might also be a good idea now to make a Scripts folder, with a Typings folder and an App folder inside, for later on.

From here, we can now start working on some kind of SPA.

Getting some typings

Firstly, let’s get the d.ts files for the libraries we’ll be using. I’ll be going for:

  • AngularJS (1.4.5)
  • JQuery (a dependency of Angular’s)

To do this. First make a Scripts folder (if there isn’t already one). Next, you’ll want to open a command line at this location. I highly recommend the Open Command Line Visual Studio extension for these sorts of things.

Once in a command line at your scripts directory, type:

typings install --save --ambient angular

The save flag means it’ll update a typings.json file with this installation. The ambient flag means we’re going to search libraries that don’t include an implementation (we’ll get the implementation of angular through bower later). When I ran this, I found that it found angular and got it from the DefinitelyTyped repository. All good there. It may well install JQuery’s typings as well, as Angular depends on it.

We’re not done yet though. You won’t currently be able to use these types in Scripts/Typings, because they’re not part of the project. But that’s okay, it’s easy to remedy.

Typings outputs 2 files after installation that you’ll find in Scripts/Typings, main.d.ts and browser.d.ts. The difference is described as so:

To simplify integration with TypeScript, two files - typings/main.d.ts and typings/browser.d.ts - are generated which reference all the typings installed in the project only one of which can be used at a time. If you’re building a front-end package it’s recommended you use typings/browser.d.ts . The browser typings are compiled by following the browser field overrides.

So, it nicely bundles up everything we install with typings into 1 usable d.ts file. Great! To get this into VS, simply right click the Typings folder under scripts (which will be empty), select Add > Existing file and add browser.d.ts (I’m not sure which was more suitable, so I picked browser). Now you can use Angular types in your Typescript files!

Getting ready for bower

For this and the next step, we’re going to be using Bower and VS’s integration with it. Happy days.

Firstly, you’ll need to go into your solution explorer and right click the project, then go to Add > New Item. From there, search under web for Bower. You’ll find “Bower configuration”. Add it. It’ll add a bower.json to your project. It also seems to add a .bowerrc file as well, and we’ll look at this first.

.bowerrc seems to configure the behaviour of Bower in JSON format. By default, bower output goes into a wwroot/lib folder. Chris S recommends using the bower_components directory instead, and I’m game to go with that. Change your .bowerrc so it looks like this.

{
    "directory": "bower_components"
}

Now, onto the bower.json file.

Getting client side packages

Your default file will look something like this:

{
  "name": "ASP.NET",
  "private": true,
  "dependencies": {
  }
}

I’m assuming the name is the name of the current thing using Bower, in this case, your project, helpfully named ASP.NET (at least, I assume we’re not changing the client side packages for ASP.NET itself).

To add in dependencies that our app depends on, you have probably figured out roughly what we do to this bower.json file. We put them in that dependencies hash there. It takes 2 strings, the name of the package and the version. So, to add in angular v1.4.5, we add in this line:

"angular": "1.4.5",

If you typed this, you’ll notice something cool, intellisense in JSON. Nice!

I added in angular, angular-route (for routing and stuff), and bootstrap (for UIs and stuff). Here’s the complete bower.json file.

{
  "name": "ASP.NET",
  "private": true,
  "dependencies": {
    "angular": "1.4.5",
    "angular-route": "1.4.5",
    "bootstrap": "3.3.5"
  }
}

Now, save that file. Now a bit more tooling magic happens. If you navigate to your project’s directory in file explorer, you’ll see a bower_components folder. And in there… you’ve guessed it… are your client side packages! VS reacts to changes to this bower.json file of its own accord.

Getting ready for gulping

We firstly need to add an NPM configuration file, for handling node modules that our project’s going to need. In this case, so far, it’s gulp, and various gulp-* modules that gulp uses. Go to add new item again, and find the npm configuration file. Keep it’s name and add it.

This “package.json” looks very like the bower.json and behaves the same way. This time, though, we want to add devDependencies, not dependencies.

Under devDependencies, in the same way as you added dependencies to the Bower.Json file, add gulp, version 3.9.1. While we’re here, we’re going to need a few (well, more like a bunch of) gulp utilities, too. Namely gulp-uglify (1.4.1), gulp-concat (2.6), gulp-inject (2.2), gulp-angular-filesort (1.1.1) and for debugging purposes gulp-print (1.2).

Your file should wind up looking like this.

{
    "version": "1.0.0",
    "name": "ASP.NET",
    "private": true,
    "devDependencies": {
      "gulp": "3.9.1",
      "gulp-uglify": "1.4.1",
      "gulp-concat": "2.6.0",
      "gulp-inject": "2.2.0",      "gulp-print": "1.2.0",
      "gulp-print": "1.2.0",
      "gulp-angular-filesort": "1.1.1"
  }
}

Save it, and VS will go off and do its thing again (if you want a peak, watch the output window). Go check in your project’s directory again, you’ll see a node_modules directory. In there, of course, is gulp! And the various other modules you just grabbed.

Setting up a gulpfile

I hope you’ve got your toolbox ready, we’re doing some plumbing. Well, pipe work, really.

First thing you’ll need to do is add a new item to the project again, this time a gulp configuration file. Leave the name as is.

You should get something like this:

/*
This file in the main entry point for defining Gulp tasks and using Gulp plugins.
Click here to learn more. http://go.microsoft.com/fwlink/?LinkId=518007
*/

var gulp = require('gulp');

gulp.task('default', function () {
    // place code for your default task here
});

We’ll ignore Microsoft’s typo in the explanatory comment at the top, shall we?

So, there’s a link we can look at to try and learn more, but we’ll stick with Chris S’s explanation for now and go to the other one if we get stuck.

You’ll probably want to open up the task runner explorer now. Go into the View menu, Other windows, and then select Task Runner Explorer. Or alternatively, ctrl+alt+backspace as a shortcut key seems to do it too. Keyboard shortcuts FTW.

As Chris S suggests, you can use console.log to output messages from your tasks. So I’ve gone ahead and done that.

gulp.task('default', function () {
    console.log("Welcome to the world of gulping");
});

Save your gulp file. Now if you look in the task runner explorer (that really needs a shorter name), you’ll see the tasks under your gulp file. If it says no tasks, try hitting the refresh button on that panel. You should see your default task. Let’s give it a whirl. Right click it and hit run.

A console window will open up next to that tree of tasks. And in it will be your message! Not bad. Now let’s make it do something useful. So let’s handle putting all the styles in our file (kind of like what the style bundling used to do).

Injecting the CSS

Firstly, we’ll need to change our index.cshtml where our app will live to have an injection point. This will be used by gulp-inject to put the relevant <link> tag.

Open up your index.cshtml file and add the following in the head tag:

<!-- styles:css -->
<!-- endinject -->

Next, we’re going to add a task to the gulpfile. Taken directly from Chris S’s example.

var gulp = require('gulp');
var inject = require('gulp-inject');
var concat = require('gulp-concat');
var print = require('gulp-print');

gulp.task('css-task', function () {
    var target = gulp.src('./views/home/index.cshtml');
    var customCssStream = gulp.src(['./bower_components/bootstrap/dist/css/bootstrap.min.css',
    './Styles/site.css']);
    return target
    .pipe(inject(
    customCssStream.pipe(print())
    .pipe(concat('appStyles.css'))
    .pipe(gulp.dest('.build/css')), { name: 'styles' })
    )
    .pipe(gulp.dest('./views/home/'));
});

Whoa. Stuff! What is this nonsense, I hear you asking. Well, Chris S again provides an explanation.

First of all we create references to the Gulp-plugins that we are going to use. Those plugins must be installed via NPM as well (I will show you the updated package.json later). You create a task using the gulp.task API passing the name of the task and a function which defines what you want the task to do. You use the gulp.src API to define the source files to be contained into the stream. Here we used the bootstrap and the custom css file for our SPA. Next we created the actuall task to be executed. Despite the fact that it seems that the inject command is the first on the pipeline, what’s is inside it comes first. Hence, first we get the css files from the customCssStream and we print them. I added the printing so that you can see that you are processing the right files. Then we concatenate those two files by using the concat Gulp-plugin. The parameter declares the resulted file name. Next, we copy the appStyles.css file inside a .build/css folder using the gulp.dest command. Finally the resulted file is being injected into a region named styles that we defined in our Views/Home/Index.cshtml

Note that at this point in his example, he hadn’t updated his package.json to have all those extra gulp bits. We’ve already done that, so we’re good.

The jist of that is that we do the following.

  1. use “require” to get all the packages we need.
  2. use gulp.src to get a file as the target variable.
  3. Get a combined stream of the css files as a variable for later use.
  4. Output the names of these css files using gulp-print
  5. Concatinate the css files you have into a new file specified in the gulp.dest function.
  6. Use inject to inject these files into the Styles region of the target file.
  7. Output this modified index.cshtml to the Views/Home directory

Seems easy when you read it like that, I guess, as opposed to that sort of weird inside out way in the gulp file.

Now, go into the Task Runner Explorer again and find the css task and run it. In the console output, you should see the names of the css files we’re injecting into that And if you go to the index.cshtml, you’ll see this:

<!-- styles:css -->
<link rel="stylesheet" href="/.build/css/appStyles.css">
<!-- endinject -->

That path looks familiar, doesn’t it? It was the path you piped your concatinated CSS files into in that gulp task. Go into your file explorer, and you’ll see said concatinated file there! Looks good to me.

Injecting the third party Javascript

First off, let’s add an injection point for the vendors JS files to our index.cshtml. Something to note, each injection point needs a start and its own closing endinject. You can’t group them under one endinject, as far as I know. Again, put this in the head tag.

<!-- vendors:js -->
<!-- endinject -->

Now, let’s add this to the gulpfile after our previous task.

var angularFilesort = require('gulp-angular-filesort');

gulp.task('vendors-task', function () {
    var target = gulp.src('./views/home/index.cshtml');
    var vendorStream = gulp.src(['./bower_components/angular-route/angular-route.js',
        './bower_components/angular/angular.js',
        './bower_components/bootstrap/dist/js/bootstrap.js',
        './bower_components/jquery/dist/jquery.js']);
    return target
        .pipe(inject(
            vendorStream.pipe(print())
            .pipe(angularFilesort())
            .pipe(concat('vendors.js'))
            .pipe(gulp.dest('.build/vendors')), { name: 'vendors' }))
        .pipe(gulp.dest('./views/home/'));
});

Looks kind of familiar. It’s virtually the same as what you were doing with the CSS, apart from that there’s more files, and we’re using the angular-filesort function to rearrange our vendorStream array’s order, to make sure it’s right for angular. Basically making sure JQuery is there for angular to use, and angular is there for angular-route to use, as far as I can tell.

Once again, if you look in your index.cshtml, it’s been inserted! This time in a script tag.

<!-- vendors:js -->
    <script src="/.build/vendors/vendors.js"></script>
    <!-- endinject -->

And that concatinated js file is where we said it would be, too. Job done! You can now use your 3rd party JS in anything inserted into index.cshtml. Which is where we’ll insert our app files.

Injecting the files under our App directory

I suggested making an app directory under scripts earlier. If you haven’t, do this now, please. We’re going to use it now. Let’s make a blank app.ts file for now. So right click the app directory, select Add and then select Typescript file and name it app.ts. We can leave it blank for now. We’ll set up a quick and easy angular app soon enough. We’re just getting the environment ready.

Now, add an injection point into your index.cshtml again. Same pattern as before.

<!-- app:js -->
<!-- endinject -->

Now, go back into your gulpfile. We’re writing very similar code to before, but also calling uglify after concatinating all the JS files under the app directory, which will essentially minify it to obfiscate our code. Here’s what we have.

gulp.task('spa-task', function () {
    var target = gulp.src('./views/home/index.cshtml');
    var appStream = gulp.src(['./scripts/app/*.js']);
    return target.pipe(inject(
        appStream
            .pipe(print())
            .pipe(concat('app.js'))
            .pipe(uglify())
            .pipe(gulp.dest('.build/spa')), { name: 'app' }))
    .pipe(gulp.dest('./views/home/'))
});

The only real new thing here is using a wildcard to get any .js files under the app directory. When you save a file in Visual Studio, it compiles the .ts file into a .js file. We then use all these js files and inject them into the view for our app.

Run that spa-task, and let’s take a look at the injection portion of our head tag of our view.

<!-- styles:css -->
<link rel="stylesheet" href="/.build/css/appStyles.css">
<!-- endinject -->
<!-- vendors:js -->
<script src="/.build/vendors/vendors.js"></script>
<!-- endinject -->
<!-- app:js -->
<script src="/.build/spa/app.js"></script>
<!-- endinject -->

Groovy!

Now let’s try running (F5ing) and taking a look at the output.

Okay, you’re page isn’t much to look at, but look at the source. You’ll see the correct link and script tags, and navigating to those gives you your concatinated stylesheets and javascript. Looking good.

If you’ve been following along with Chris S, he signs off at this point. Thanks Chris! He’s done more with actually using Angular, but we’re going to cheat a bit and just get something real basic going later on. There’s a quick thing I want to do with gulp first though.

Running the tasks automagically

That’s all good, but remembering to run those tasks all the time would be a pain. So let’s set it up so it does all of these every time we build. Go into the task runner explorer.

From here, next to the tree where the tasks are, you’ll see a bindings tab, in the same part where the console is. Select the bindings tab. Here are all the various bindings that can be used, or triggers for the gulp tasks to run in other words.

What we want is to run all 3 of our tasks (css-task, vendors-task and spa-task) after the build finishes (and therefore the js files are compiled).

For each of those tasks, right click them and under the bindings submenu, select after build. And that’s it. Do a build to double check.

Now, this will happen every time you build. No need to worry about manual runs anymore.

Now, to test that the app is all working as expected, let’s put together a basic angular app.

Quick and dirty Angular app

Our objective here is just to see that angular is all set up nicely with our typescript and VS, really.

Let’s write a quick controller into our app. We’re going to make a beer app (sorry, it’s late and I’m looking for inspiration). If you don’t like beer, replace it with wines, fizzy drinks, cats, Pokemon, stocks and shares or something else you like.

In app.ts:

module Sample {
    var app = angular.module("beerApp", [])
        .controller("beerController", ($scope) => {
            $scope.beers = [
                "Corona",
                "Carlsberg",
                "Cronenberg",
                "Heiniken",
                "London Pride",
                "Sam Adams",
                "John Smith's",
                "Peroni"
            ];
        });
}

And now we can change the main index page to use this app, like so.

<body>
    <main ng-app="beerApp">
        <div ng-controller="beerController">
            <p>You can select from any of the following beers:</p>
            <ul>
                <li ng-repeat="beer in beers">
                    {{beer}}
                </li>
            </ul>
        </div>
    </main>
</body>

Build the project (so all the js files rebuild and concat themselves again, and view your page. Voila! A list of beers. Congratulations, you probably deserve one.

Modularization

I had a little trouble with this at first, so I thought this was worth covering. Ideally, we don’t want our controllers merely to be a function in the main app.ts, but their own separate files, possibly in different modules, etc. A little bit of extra work, but is worth it in the long run. My reading for this came from this Scott Logic post on typescript and Angularjs.

First, let’s make a little change to the app.ts.

module Sample {
    angular.module("beerApp", []);
}

What we’re doing here is removing the controller attachment at this point, as I’ve found out, you can run into file ordering problems if you’re not careful.

Now, let’s add a beerController.ts to the app directory.

module Sample {
    export interface IBeerScope extends ng.IScope {
        beers: string[];
    }

    export class BeerController {
        // needed so that uglify doesn't minify the special $scope variable name away
        public static $inject = ['$scope'];

        constructor(private $scope: IBeerScope) {
            $scope.beers = [
                "Corona",
                "Carlsberg",
                "Cronenberg",
                "Heiniken",
                "London Pride",
                "Sam Adams",
                "John Smith's",
                "Peroni"
            ];
        }
    }

    angular.module("beerApp").controller("beerController", BeerController);
}

First off, to be good Typescript developers, we want our variables to be typed. And a $scope variable is passed into our controller’s constructor. The Angular definitions has an IScope interface, which has all the common scope members that are available from any $scope. You’ll be unsurprised to hear that that doesn’t include a beers variable. So our interface at the top extends IScope with our own additional implementation, and we use that later.

We then set up our class. We use a public static $inject array to say what variables will be passed into the constructor from Angular. This seems to stop minification squashing those special variable names, such as $scope, which carry special meaning to Angular.

We then have our constructor, which is where the magic happens. With the passed in scope having a type that includes a beers field, we can now assign to it. And we do! Notice the private before the $scope? That’s a bit of Typescript magic, automatic assignment of constructor parameters to fields. In other words, $scope is also a field we can now use in the rest of the class. See this post by Steve Fenton for more info on that.

Lastly, we reach out to angular’s module system for the beerApp, and we attach a controller to it, with this class as the second argument. The constructor for this class will be used when that controller is needed now, and this class’s members will be available where this controller is used in a template.

Build, refresh. And feel satisfied. It still works! And is much neater and tidier now.

Conclusion

I hope you’ve found this blog post useful, educational, or at the very least interesting. This has been a very interesting weekend for me in a development sense. I feel like I’ve moved from curious skepticism to acceptance in a relatively short space of time. I wouldn’t say just because I’ve done a project in it with someone guiding me that I’ve grokked it fully or anything, but I don’t feel so confused as to how this way of working would ever fit together. On the contrary, I’m looking forward to working with these technologies more in the future!

Thanks to Christos S for his blog post which lead me to try doing things this way. I might have still been frantically searching for someone actually using these tools together in a comprehensive guide in a similar way to yours. Also thanks to Bjørn Sørensen for your help with my StackOverFlow Question that came up as a result of writing this blog post. It helped me fix the modularization section, which might well have been dropped if I couldn’t have gotten it to work. Also thanks to the array of different writers on these subjects that I’ve linked to, it’s all helped me (and probably others) get to grips with these areas.

If you’ve got any personal experiences akin to this related to the 2k16 toolchain or just general opinion on this, feel free to leave your comments.

Written with StackEdit.


  1. Worth mentioning regarding NODE, I really like Node.JS, I think it’s great and probably the best way to write small apps as of now, since almost everyone knows JS. But the NPM thing stumped me. A story for another time.
  2. Even though I’m using Angular here in this post, there are plenty of other frameworks out there this will work with. The JS guru I mention in this article suggests that Auralia is the new hotness, though this one does require JSPM to get.
  3. Turns out the angularjs typescript files are on NuGet, but called angularjs.typescript (I think). Merely searching for them with terms like angular, angular typescript, angularjs typescript doesn’t seem to find them, or it didn’t for me. But by this point I’d decided to try the new way.

2 comments:

As always, feel free to leave a comment.