Migrating an Angular 1 app to Typescript with Modules

Migrating an Angular App to Typescript with optional ES6 Modules

I know, I know, Angular is pretty outdated, but some of us have apps in production running on Angular and would like to introduce typescript anyways… This being said, working with an Angular 1 app with typescript you’re in a really weird place right now. Half of the time when googling “angular typescript” stuff, you’ll find angular 2 answers, and in order to get angular 1 closer to angular 2, the angular team introduced even more concepts to the already overloaded angular 1… But that’s just how it is.

Assuming ui-router is your routing solution, here are the steps for migrating an existing angular app to typescript.

The Easy Way: Without Modules

1) Rename all your js files to ts files

That’s an easy one, just rename all the files. If you have many files, you could use something like the following python script:

import os

for path, subdirs, files in os.walk("./"):
 for name in files:
 oldPath = os.path.join(path, name)
 newPath = oldPath.replace(".js", ".ts")
 os.rename(oldPath,newPath)

2) Install Typings

Angular 1 is written in js not in ts. So in order to get code completion from your IDE and tell typescript the “angular” object exists, you need to get the type definitions. You could skip this step, but it’s not a lot of effort and prevents wiggly lines in your editor and errors from the ts compiler (but it would transpile anyway).  To do so, install “typings” and then run typings install with the following typings.json file:

{
  "globalDependencies": {
    "angular": "registry:dt/angular#1.5.0+20160819155010",
    "angular-ui-router": "registry:dt/angular-ui-router#1.1.5+20160810191828",
    "jquery": "registry:dt/jquery#1.10.0+20160704162008"
  }
}

If you don’t want modules, that’s kind of all there is to it.

3) Compile your code

Put this tsconfig.json at the root of your project

{
  "compilerOptions": {
    "target": "es5"
  }
}

and then run tsc in the command line (also from the root of your project). This assumes you have the typescript compiler installed.

The Hard Way: With Modules

If you don’t know the basics of ES6 modules, maybe it’s a good idea to read this article on typescript with ES6 modules first.

In order to get things running with modules, a little more work is required. Here is a complete working example, that will be discussed in the following.

To use modules, you’ll need a module loader, here we’ll use SystemJS. You can start setting up everying like without modules, except that your tsconfig should now be:

{
  "compilerOptions": {
    "module": "system",
    "target": "es5"
  }
}

4) Loading the Main FIle With SystemJs

For using modules, you want to have one main module that you import using SystemJs. But if you just import your “app.js” with SystemJs (like so System.import('app.js')), then it will break your app. The reason for this is that ng-app="myApp" (or however you called your app) will be loaded before app.js is loaded. So here’s the first step. One possible solution for this is to bootstrap manually, so your index.html would read:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Typescript Modules</title>
    
    <!-- get angular -->
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.3.1/angular-ui-router.min.js"></script>
    
    <!-- get systemJS from somewhere -->
    <script src="https://code.angularjs.org/tools/system.js"></script>
    
    <script>
      System.config({
        baseURL: './',
        defaultJSExtensions: true
      })
    </script>
    <script>
      System.import('app.js').then(function() {
        angular.bootstrap(document, ['myApp']);
      });

    </script>


  </head>
  <body>


    <div ui-view></div>
    <!-- We'll also add some navigation: -->
    <a ui-sref="state1">State 1</a>
    <a ui-sref="state2">State 2</a>

  
  </body>
</html>

Note how there is no ng-app attribute in this code anymore!

(Another other possibility is to also load Angular with SystemJS, but this is not discussed here)

5) Rewriting Your Controllers, Services, Directives and Components to Module Syntax

Since Angular 1.5  Components are the suggested way to do things, but let’s say you still have some 1.4 code and you’re using controllers… Then those controllers would have to be rewritten like so:

export class HelloWorld {

  static $inject = ["$scope"];

  constructor (private $scope) {
    $scope.name = "World"
  }

}

This is “not recommended” as you should use “controllerAs” syntax or components, but if you’ve got a lot of code, you’ll be just glad there is a way of doing it without “controllerAs” syntax

6) Importing

Now comes the fun part, you can import your controllers. This would be the app.ts :

import {HelloWorld} from "./hello-world";
import {GoodbyeWorld} from "./goodbye-world";

var myApp = angular.module('myApp', ['ui.router']);


myApp.config(function($stateProvider, $urlRouterProvider) {
  //
  // For any unmatched url, redirect to /state1
  $urlRouterProvider.otherwise("/state1");
  //
  // Now set up the states
  $stateProvider
      .state('state1', {
        url: "/state1",
        templateUrl: "partials/state1.html",
        controller: HelloWorld
      })
      .state('state2', {
        url: "/state2",
        templateUrl: "partials/state2.html",
        controller: GoodbyeWorld
      });

That’s it. That’s how you’d use modules with systemJS and typescript. Again, here’s the complete code: Final Code.

Leave a Reply

Your email address will not be published.