AngularJS 2 is coming. And the momentum towards it is building quickly. At the latest ng-conf, the AngularJS folks revealed a bit more about their thoughts, principles and yes, code! AngularJS 1.x has been great, and has allowed us to do a lot of  cool things, but AngularJS 2 is better, faster and more importantly, less code and easier (Yes, it’s true!).

You might have heard and read a lot of articles online which try and make AngularJS 2 a big scary thing and that the world is ending. We are here to assure you that it is, in fact, not! Migrating to AngularJS is work – it is not a simple drop and replace change. But that said, there are things you can start doing in your AngularJS 1.x code base today, that will make it easier for you start using AngularJS 2 when it is ready for prime time.

As part of this series, we will explore the various steps you can take from controllers to services to directives and more.

This particular article aims to provide insight on how you can start changing your AngularJS 1.x controller  to make it easier to migrate to AngularJS 2, and what changes are important in this.

An AngularJS 1.x controller

Here is what a simple AngularJS 1.x controller might look like. This syntax is familiar, and will be extremely common prior to AngularJS 1.2.


app.controller('myController',['$log', '$http', '$scope', 
    function($log, $http, $scope) {
  $scope.value = 'someValue';
  $http.get('someDataUrl').then(function(data){
    $scope.value = data;
  });
  $scope.clickEvent = function(data){
    // Click event of any element.
  }
}])
<div ng-controller="myController">
    <input type="text" ng-model="value">
    <button ng-click="clickEvent()"></button>
</div>

Steps to Migrate

How can we migrate to AngularJS 2 controllers in a steady, step by step manner? There are a few steps to making our AngularJS 1.x controllers look and feel like AngularJS 2 controllers.

1. Avoid using $scope / Use controllerAs Syntax

The first thing you should start doing, even if you are not using or planning to switch to AngularJS 2 is avoid using $scope and start using the controllerAs syntax which was introduced in AngularJS 1.2.

Why should we do this? Well, for one, consider the case where you have nested controllers. In such a case, suppose

  • We have a boolean canShow in our parent controller
  • We have nested HTML with its own child controller.
  • Within that HTML, we want a checkbox that is bound to canShow from the parent controller

For this, you will have to bind the checkbox to $parent.canShow, because of how prototypical inheritance works in JavaScript. Otherwise, if we just have canShow, we end up creating a new property on the child controllers scope without ever affecting or changing the parent.

With the controllerAs syntax, this becomes a lot clearer and more declarative. Please refer to John Papa’s style guide for more details on controllerAs syntax and lot more.

The previous controller we had might change as follows when we switch to using this syntax:


app.controller('myController',['$log', '$http', function($log, $http){
  this.value = 'someValue';
  $http.get('someDataUrl').then(function(data){
    this.value = data;
  });
  this.clickEvent = function(data){
    // Click event of any element.
  }
}])
<div ng-controller="myController as myCtrl">
    <input type="text" ng-model="myCtrl.value">
    <button ng-click="myCtrl.clickEvent()"></button>
</div>

Define controller as a class

The second simple step we can take to prepare ourselves for AngularJS 2 is to start moving to declaring our controllers as separate JavaScript classes, and then just passing the class reference to the .controller function of AngularJS. AngularJS 2 controllers are just simple JavaScript classes, and this steps makes it easier for us to transition later.

For us in AngularJS 1.x, this is a very trivial change, and might look something like below:


function myController($log, $http) {
  this.value = 'someValue';
  $http.get('someDataUrl').then(function(data){
    this.value = data;
  });
  this.clickEvent = function(data){
    // Click event of any element.
  }
}
app.controller('myController',['$log', '$http', myController])
<div ng-controller="myController as myCtrl">
    <input type="text" ng-model="myCtrl.value">
    <button ng-click="myCtrl.clickEvent()"></button>
</div>

Activate function for load

Currently, with AngularJS 1.x, we perform all the initialization logic of our controller in the controller function itself. So we define the variables, properties and functions, and also load the relevant data along with it.

AngularJS 1.4 Router (which will also be the AngularJS 2 router) provides with a ‘activate‘ hook. If in your controller class you have a function called activate defined, then this function will be called by the AngularJS router when the view is rendered.

The advantages of having all your initialization logic inside an activate() function are as follows:

  • It holds the initialization logic for your controller in a single place instead of scattered over the controller across potentially hundreds of lines.
  • The new router for AngularJS will automatically call the activate() function when the view is rendered.
  • This also increases the testability of your controller because you can test the logic of your controller without ever initializing the activate function as part of the test.

If we do this with our current code, without the new router, our controller will change as below.


function myController($log, $http) {
  this.value = 'someValue';
  this.activate = function(){
    $http.get('someUrl').then(function(data){
      this.value = data;
    });
  };
  this.clickEvent = function(data){
    // Click event of any element.
  }
  
  // Finally, till the new router
  this.activate();
}

app.controller('myController',['$log', '$http', myController])
<div ng-controller="myController as myCtrl">
    <input type="text" ng-model="myCtrl.value">
    <button ng-click="myCtrl.clickEvent()"></button>
</div>

Optional – Use ES6 if possible

An optional step to make it even easier to transition is to use the new ES6 syntax for your application, and transpile your JavaScript from ES6 to ES5.

How might our controller look in ES6?


export class myController {
  value;
  constructor($log, $http) {
    this.value = 'someValue';
    this.$http = $http;
    // Till the new router
    this.activate();
  }
  activate() {
    this.$http.get('someDataUrl').then(function(data) {
      this.value = data;
    });
  }
  clickEvent(data) {
    // Click event of any element.
  }
}
<div ng-controller="myController as myCtrl">
    <input type="text" ng-model="myCtrl.value">
    <button ng-click="myCtrl.clickEvent()"></button>
</div>

AngularJS 2 controllers

Now that we have done all this, how would an AngularJS 2 controller look like? Glad you asked:


import {IHttpService, ILogService} from 'angular/angular2/somewhere';
export class myController {
  value;
  constructor($log:ILogService, $http:IHttpService) {
    this.value = 'Default Value';
    this.httpService = $http;
  }
  activate() {
    this.httpService.get().then(function(data){
      this.value = data;
    });
  }
  clickEvent(data) {
    // Click event of any element.
  }
}
<div ng-controller="myController as myCtrl">
    <input type="text" ng-model="myCtrl.value">
    <button ng-click="myCtrl.clickEvent()"></button>
</div>

If you notice, it is almost exactly the same as what we had written as an ES6 AngularJS 1.x controller, with the addition of optional types using TypeScript (which you could also use today). You could avoid them, but it gives us so many nice things that its worth having.

As you can see, you can actually start writing AngularJS 2 controllers today! So start getting your code ready now!

CAUTION: This syntax is based on the proposal made in ng-conf 2015 and may be changed in future. Please refer to the AngularJS 2 home page for latest updates.

Parth MistryMigrating to AngularJS 2 Controllers