Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Preparing for Angular 2

DZone's Guide to

Preparing for Angular 2

A Collection of Best Practices to be Ready For Angular 2

· Web Dev Zone
Free Resource

Discover how to focus on operators for Reactive Programming and how they are essential to react to data in your application.  Brought to you in partnership with Wakanda

I'm sure you heard about Angular 2 and that it will be totally different. Forget everything you know and start from scratch. Jokes aside, if you have taken a closer look you already know that, yes, it will be new, things will be different (as it's mostly the case with new stuff), but many concepts will still be there. Well, as Angular 2 starts getting more and more concrete, articles, videos and podcasts get published that contains lots of useful information on how to get prepared an eventual migration scenario. I use this article to collect such best practices for myself and and obviously to share them with you!

Totally feel free to submit new practices in the comments, or directly submit a PR in the github repo for my blog.

Get rid of $scope: controller-as syntax

Definitely switch over to the controller-as syntax and get rid of $scope in your controller files as well as in the templates.

<div ng-controller="PersonController as personCtrl">
    <div ng-repeat="person in personCtrl.people">
        {{ person.name }}
    </div>
    <button ng-click="personCtrl.add()">Add</button>
</div>

angular
    .module('demo', [])
    .controller('PersonController', PersonController);

function PersonController(){
    this.people = [
        {
            name: 'Juri'
        },
        {
            name: 'Steffi'
        }
    ];
};

First of all, Angular 2 won't have any concept of $scope and then - especially in the HTML - using the controller-as syntax avoids a lot of headache when it comes to nested controllers.

Links

Use directive controllers where possible

When you create new Angular directives you actually have different possibilities where to define your logic:

angular
    .module('demo', [])
    .directive('personDisplay', PersonDisplayDirective);

function PersonDisplayDirective(){
    return {
        compile: function(element, attrs){
            // implement logic
        },
        link: function($scope, iElement, iAttrs){
            // implement logic
        },
        controller: function(){
            // implement logic
        }
    }
}

When should we use which?? That's a common question among Angular devs. You can find some suggestions on the Angular docs and in various online communities, but it's not always that clear.

Angular 2 removes all of these, you'll have a component (directive) and a controller class associated to it. As an obvious result, people suggest to use "directive controllers" wherever possible.

angular
    .module('demo', [])
    .directive('personDisplay', PersonDisplayDirective);

function PersonDisplayDirective(){
    return {
        controller: function(){
            // implement logic
        },
        controllerAs: 'vm'
    }
}

Image title


From his perspective:

  • No you shouldn't avoid them per se. The point is, as long as you do stuff that hasn't to be in compile/pre/postlink, put it in ctrl
  • In other words, in most of the cases you actually don't need to deal with compile/link. Controller gives you everything you need
  • Except for cases like ngModelController, which registers itself at ngFormController during preLink
  • However, when controller is enough, use that. You don't have to deal with rather complicated compile/link things.
  • In addition, it's much more aligned with the philosophy behind Angular 2 components. I recommend using controllers where possible
  • Also easier to test!

Note that one issue when you use directive controllers is often on how to reference the directive scope properties from within the controller - since we should possibly avoid $scope. Since Angular v1.3 there is the boolean bindToController property and recently in v1.4 they've even improved it s.t. you can write things like

return {
  restrict: '...',
  bindToController: {
    val1: '=',
    val2: '@'
    ...
  },
  controller: function($log){
    // access them with
    $log.debug(this.val1);
  }
}

There's a nice article on Thoughtram about that. Follow the link below.

Links

Prefer Components over Controllers

Angular 2 follows the current trend of web components. Thus, it won't have autonomous controllers any more, but just in conjunction with so-called components. An Angular 2 app is a tree of nested components, with a top-level component being the app.

So rather than having controllers, start to create such widgets or components that are autonomous. In Angular 1.x we know them as directives. Search for template dependent controllers and try to move them into separate directives. For example, refactoring the previous example we could get something like this:

<div ng-controller="PersonController as personCtrl">
    <!-- PersonController scope --> 

    <people-list people="personCtrl.people">
        <!-- PersonListController scope here -->

        <div ng-repeat="person in personListCtrl.people">
            {{ person.name }}
        </div>

        <button ng-click="personListCtrl.add()">Add</button>
    </people-list>
</div>

Note that PersonController passes in the data required by our list component. Thus it remains fairly re-usable. All it does is creating the UI functionality.

angular
    .module('demo', [])
    ...
    .directive('peopleList', peopleListDirective);

function peopleListDirective(){
    return {
        controllerAs: 'personListCtrl',
        controller: function($scope, $attrs){
            this.peopleList = $scope.eval($attrs.people);

            this.add = function(){
                // implementation
            }.bind(this);
        }
    }
}

Use Observables instead of $watch

David East recently spoke at Angular U on how to prepare for upgrading to NG 2. What's particularly interesting is his usage of Rx.js to avoid $watch.

angular.module('demo', [])
    .service('peopleService', PeopleService);

function PeopleService() {
  var peopleSubject = new Rx.ReplaySubject();

  var service = {
    subscribe: function(subscription) {
      peopleSubject.subscribe(subscription);
    },
    add: function(people) {
      people.push({
        name: 'Name ' + (people.length + 1)
      });

      // broadcast
      peopleSubject.onNext(people);
    }
  };
  return service;
}

At this point, on the PeopleController one can subscribe to the changes

function PersonController(peopleService) {
  var vm = this;
  vm.people = [];

  peopleService.subscribe(function(people) {
    vm.people = people;
  });
}

In the people-list directive, that same people service can be used to broadcast new changes

function peopleListDirective() {
  return {
    controllerAs: 'peopleListCtrl',
    controller: function($scope, $attrs, peopleService) {
      this.people = $scope.$eval($attrs.people);

      this.add = function() {
        peopleService.add(this.people);
      }.bind(this);
    }
  }
}


See the sample functionality on Plunker


Links

Presentation by David East on preparing for Angular 2:

Reactive Extensions - Rx.js

Learn how divergent branches can appear in your repository and how to better understand why they are called “branches".  Brought to you in partnership with Wakanda

Topics:
angularjs ,angularjs 2

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}