Using Typescript, Angular, Require and existing JavaScript libraries: The great altogether

-

Welcome to my first blog post for Luminis! This time I am not going to blog about .Net related stuff, as I started working on a project which uses all the above technologies together.

It took a little time to get my head around it all and I bumped into quite a few things when combining these cool frameworks, so I wanted to share my findings with you all.

First off: Typescript and Angular together

Typescript is a cool new language that makes it a lot easier to write structured and object oriented JavaScript. If you come from a language like C# or Java you will feel right at home. It has classes, interfaces, strongly typedness and a lot more cool stuff to write your applications. Another cool feature is that it compiles to just JavaScript! So if you have been doing a lot of object oriented JavaScript, using the revealing module pattern or class patterns to write classes and modules you will be pleased to hear that typescript takes that pain away. I am not going to give a complete typescript tutorial or description, you can find those here http://www.typescriptlang.org/.

Now take Angular, the cool MVC. MVVM JavaScript framework. Angular has modules, databinding, controllers, services, all the cool stuff we use in object oriented languages to separate our concerns. If you come from  C# and XAML, Angular can definitely be compared to PRISM, only for JavaScript. And that is exactly my problem, you have a cool structured framework like Angular, with an unstructured, non-oo language like JavaScript. Let’s combine the two!

Let’s create a very simple Angular web application. Almost no UI, just to understand what is going on. The tooling I am going to use is Visual Studio 2013, it get’s Angular and Typescript, but you could also use Web storm for example. Typescript is completely open source and already has a lot of support.

In the figure below you see my attempt to set up a simple hello world controller, you can see Visual Studio, which is great tooling, does not has a lot of intellisense to offer me, even though I reference angular with an intellisense comment. This is due to the dynamic nature of JavaScript.

In a minute I will show you the complete app, but this tooling trouble, is one of the problems Typescript aims to solve so stay with me. Now on to the complete example.


  
    
    Typescript HTML App
     
      
      
 
    
    
      

Typescript Angular App

{{ourController.sayHello()}}

Notice a couple things about this html. First of the multiple script references. This can become a hassle. The controller has to come after Angular. The order of the scripts matter, when I create a lot of files and dependencies this becomes hard to manage. Will solve this later using RequireJs. Further more on line 8 I am using the as syntax to define a controller. This allows us to bind to properties defined on our class instead of properties defined on our $scope. Let’s have a look at the JavaScript:

/// 
 
  var HelloWorldController = (function () {
     function HelloWorldController() {
     }
     HelloWorldController.prototype.sayHello = function () {
         return "Hello world!";
    };
     return HelloWorldController;
 })();
 
 var module = angular.module('appModule', []);
 
 module.controller('appController',HelloWorldController);

Notice the whole anonymous function part? This is the JavaScript pattern to define a class. In JavaScript a class is basically just another function. It get’s created in an anonymous function to prevent the global scope from polluting and in this anonymous function it also get’s populated with variables and methods. This is a lot of work for just a class! Let’s convert this sample to Typescript!

class appController {
 
    constructor() {
        this.name = "Chris";
    }
    name: string
    sayHello(): string {
        return "Hello " + this.name;
    }
}

Look at this!  If you come from C# or Java or C++ even you will feel right at home! Very nice. And this just compiles to plain JavaScript, so in the browser you will just reference the same .js file as before. Now let’s make use of angular. The controller still needs to get registered in Angular. As Typescript is a superset of JavaScript we can do the Angular registration below the class definition, or in a separate typescript file. The only problem is that we will get compiler errors as the typescript compiler does not know about Angular or, other normal JavaScript libraries. To get Typescript to play nice with Angular and other JavaScript libraries you can make use of so called definition files. These are basically descriptions of a JavaScript library so that the Typescript compiler knows which functions there are, what they return and parameters they have. You can create these, there is a big tutorial on these on typescriptlang. Luckily most of them are already there and you can download them from https://github.com/borisyankov/DefinitelyTyped. When you use Visual Studio 2013 you can right click on a JavaScript file and download the files from there. Let’s update our code with an Angular definition file.

I created a screenshot of my Visual Studio just to let you see how cool this is. In the top of the file I reference the Angular definition file. Now Typescript knows of the angular variable and gives me complete intellisense about types, function etc. You can see this function returns an ng.IModule instance, on which we also get complete intellisense. Here is the complete code. Keep in mind that this is just compile time. Run time the scripts still need to be included in the right order to make sure the angular global exists before our controller registration.

/// 
 
class appController {
 
    constructor() {
        this.name = "Chris";
    }
    name: string
 
    sayHello(): string {
        return "Hello " + this.name;
    }
}
 
var appMpdule: ng.IModule = angular.module("appModule", []);
appMpdule.controller("appController", appController);

What is also cool, is that if define parameters in our constructor, they will get inject by angular. We could let inject Angular all kinds of registered services just like with normal controller functions!

Let’s create a cool alert service which our controllers can use. Here is the code for our alert service.

class defaultAlertService implements alertService {
  showAlert(text: string): void {
    alert(text);
  }
}
 
interface alertService {
   showAlert(text: string): void;
}
var servicesModule: ng.IModule = angular.module("services", []);
servicesModule.factory("alertService", () => new defaultAlertService());

Our service implements an interface, so we can easily switch it for a service which uses bootstrap for example. Also on line 14 you can see the angular registration. Yes Typescript has lambda expressions! The difference with a normal anonymous function is that the lambda keeps the this pointer of the parent function instead of a new scope. Now the service needs to get injected in our controller. Here is our controller with it’s new showAlert function. It get’s called when someone clicks a button. The html will follow later.

/// 
class appController {
    constructor(private alertService:alertService) {
        this.name = "Chris";
    }
    name: string
    sayHello(): string {
         return "Hello " + this.name;
    }
    showAlert(text: string) {
        this.alertService.showAlert(text);
    }
}
var appMpdule: ng.IModule = angular.module("appModule", ["services"]);
appMpdule.controller("appController", appController);

On line 5 you see a cool Typescript construction. For a constructor parameter that has a modifier Typescript will automatically emit a class variable. If the name of our parameter is the same as the name of the registered service Angular will just inject it. If not, when you use minimizing you have to use another inline annotation for it to work. You can also see the dependency on the services module when loading our appmodule. Keep in mind that this is not a file dependency. That is still up to us. Look in the html next, there we will need to include the scripts in the right order. Angular –> Service –> Controller. It is simple right now, but this can quickly grow and will lead it’s own life.


  
    
    Typescript HTML App
    
    
 
    
    
 
  
  
    

Typescript Angular App

{{ourController.sayHello()}}

You can see, starting on line 6, the script tags. They have to be in this order or the app will break. On top of that, the browser will load all these scripts, even if there are services included that the user does not need because he does not touch the functionality tat requires these services. Enter RequireJS

Typescript and RequireJS

Let’s take a look at RequireJS. This is a library that adds modularization to JavaScript applications. Uhm wait, modularization? Doesn’t Angular also have modules? Yes it does! But Angular has logical modules. Angular does not modularize the file loading for example, or the handles the file dependencies as you can see in our index.html. Require can work neatly together with Angular modules, keep in mind that both libraries solve different problems. You can find everything about RequireJS here: http://www.requirejs.org/docs/api.html. let’s have a look. I am going to add Require to my application. The first thing that will change is my index.html.

Two things that stand out are there is only one script tag left in our html. That is the reference to Require. As Require will manage all our script loading, this is the only thing we need in our main html page. The second thing is the data-main attribute on the script tag. This tells Require where it should start with executing. Very comparable to a main function in C for example. next we have a look at the contents of our main file.

require.config({
 
    paths: {
        'angular': '/Scripts/Angular/angular'
 
    },
 
    shim: {
        'angular': {
            exports: 'angular'
        }
    }
});
 
// Start the main app logic.
require(['angular', '../Home/js/appController'],
function (angular) {
    var elem = document.getElementsByName("html")[0];
    angular.bootstrap(elem, ['appModule'])
});

First in our main js file, I call require.config. We can use this function to give require some extra config stuff. Like short names for libraries. My call tells Require that whenever I want to use angular, it can be found by following the path I pass to it. On line 8 you see another cool thing. It’s a so called shim. Require loads JavaScript files and sees them as modules. But those JavaScript files ideally should know that they contain Require modules. When defining an Require module a JavaScript file should tell Require what it is exporting and on what other modules it depends. We shall have a look at that later. The problem with angular is that this is an existing JavaScript library that does not contain Require modules. Angular just populates the global scope with an angular variable. By using a shim, we tell Require that when angular is done loading, it should clean up the global angular variable and instead pass it to the function on line 17. This function get’s executed when angular is done loading, and it’s dependency, our appController  is done loading. You can see  that this call to require, executes and when all the modules in the parameters are done loading.

If you are paying attention you are probably wondering why we don’t have to shim our appController, as I said earlier that JavaScript files should tell require what modules they are exporting. This is because I made a little modification in our appController. Here is the code

/// 
/// 
 
import ServicesModule = require("../../Services/alertService");
 
export class appController {
 
    constructor(private alertService:ServicesModule.alertService) {
        this.name = "Chris";
    }
    name: string
 
    sayHello(): string {
        return "Hello " + this.name;
    }
    showAlert(text: string) {
        this.alertService.showAlert(text);
    }
}
 
 
import angular = require('angular');
 
var appMpdule: ng.IModule = angular.module("appModule", ["services"]);
appMpdule.controller("appController", appController);

Couple of cool things here. On line two you can see a typescript statement that is unknown to us. This is an undocumented feature of typescript. This statement allows us to specify the Require dependencies of our current module. officially, the import statement should also take care of this, but this doesn’t work when I am not using the module in a statement in my code, I only use types from the module to type parameters, these won’t be there in the resulting JavaScript so Typescript decides it does not need my module. the fix is to also specify the <amd thingy..

On line 4 you can see the import statement. This makes sure I can use types from the module and the module itself in my code.

On line 6 you can see an export keyword. This instructs the Typescript compiler to define a Require module that exports our controller. On line 16 you see that we need to import angular to make use of Angular. Just as with our alertService we want to make use of the angular module, so we add an import statement. This leads to compiler errors however. Because unlike our  alertService, the angular library is no typescript, and the angular.d file does not tells the compiler that angular in our project get’s loaded as a Require module. So we need to write a new definition file that tells typescript that angular is loaded as a Require module, and from that module exports the angular variable. Basically we need to make Typescript aware of our Require configuration in our main.js file. Here is the .d file to do that.

/// 
 
 
declare module "angular" {
    export = angular;
}

You can see the file getting referenced on line 1 or our controller and also in our alertService. Because this file get’s referenced Typescript knows of the angular module that exports the angular variable. Just as the shim configuration for require in the beginning of this post. The alertService is defined below

/// 
 
export class defaultAlertService implements alertService {
    showAlert(text: string): void {
        alert(text);
    }
}
 
export interface alertService {
 
    showAlert(text: string): void;
}
 
import angular = require('angular');
 
var servicesModule: ng.IModule = angular.module("services", []);
servicesModule.factory("alertService", () => new defaultAlertService());

Nothing strange here. It is just like our controller. We use export to export the different types from this module. But there is still something strange happening. When we load our application, I get no errors at all, but it also isn’t working correctly.

The problem here comes from the fact that Require loads and bootstraps angular before the DOM is ready. What we want, is a way to tell require load the modules as soon as the dom is ready. Fortunately there is a way to this. This is actually a RequireJS plugin called domReady. It is really cool. Just download the domReady.js file. And make the domReady a module dependency of your main module. Here is the modified main.js.


require.config({
 
    paths: {
        'angular': '/TypeScriptAngular//Scripts/Angular/angular',
        'domReady': '/TypeScriptAngular//Scripts/domReady'
    }
    ,
 
    shim: {
        'angular': {
            exports: 'angular'
        }
    }
});
 
// Start the main app logic.
require(['domReady', 'angular', '../Home/js/appController'],
function (domReady, angular) {
    domReady(function (doc) {
        angular.bootstrap(doc, ['appModule']);
    });
 
});

You can see that when you add domReady as a dependency, it will give you the current document as a parameter to your module function.

So now we are done! Everything works, is object oriented by using typescript, is MVC’d by using angular and asynchronously loads our modules and dependencies when we need them. Typescript makes working with require a lot easier, but you do have to know how Typescript accomplishes this, and how to make use of the .d files.

Here is the code by the way! See you next time!