Converting to Angular 1.5: from directives to components

We are busy upgrading our projects to Angular 1.5 in preparation for Angular 2.

Here is a quick walkthrough of converting a basic angular directive to component.

Angular Directive

1
2
3
4
5
6
7
8
9
10
11
12
13
angular.module(‘app’).directive(‘contactForm’, function() {
    return {
        email: ‘=?’,
        name: ‘@’,
        surname: ‘@’
    },
    controller: function($scope, sendEmail) {
        $scope.send = function() {
            sendEmail.send($scope.email, $scope.name, $scope.surname);
        };
    },
    templateUrl: ‘views/templates/contactForm.html’
});

I have, in this example, made email a two way data binding, and name and surname just strings.

Template

Contact Form template

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div class="form-wrapper">

    <form>
    <p>
        <label$gt;Email</label>
        <input type=“text” name=“email” ng-model="email" />
    </p>
    <p>
        label>Name</label>
        <input type=“text” name=“name” ng-model=“name />
    </p>
    <p>
        <label>Surname</label>
        <input type=“text” name=“surname” ng-model=“surname” />
    </p>
    </form>

</div>

Calling the directive in another view:

Directive bindings:

  • @ String
  • & One-way Binding
  • = Two-way Binding

Component

Step 1:

Replace “.directive” with “.component”

Step 2:

Don’t pass a function as the second argument, pass an object with fields

You should now have

1
2
3
angular.module(‘app’).component(‘contactForm’, {
    …
});

Note the { … } instead of the function() {…}

Step 3:

Bind variables using “bindings: { .. }” instead of “scope: { … }”

You should now have

1
2
3
4
5
6
7
angular.module(‘app’).component(‘contactForm’, {
    bindings: {
        email: ‘=?’,
        name: ‘@’,
        surname: ‘@’
    }
});

Component binding types:

  • = Two-way Binding
  • < One-way Binding
  • @ String
  • & Callbacks (to output to parent scope)

Step 4:

Add controller, similar to directives, except you no longer pass in $scope. The variables bind on “$ctrl” the default value for controllerAs.

You can assign controllerAs to any value, but for this example it is unnecessary.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
angular.module(‘app’).component(‘contactForm’, {
    bindings: {
        email: ‘=?’,
        name: ‘@’,
        surname: ‘@’
    },
    controller: function(sendEmail) {
   
        var $ctrl = this;

        $ctrl.send = function() {
            sendEmail.($ctrl.email, $ctrl.name, $ctrl.surname);
        }
    },
    templateUrl: "/path/to/template/feature.template.html"
});

Note

1
var $ctrl = this;

Explicitly give $ctrl the controller’s scope.

Replace $scope with $ctrl. In the template you will have to reference each variable with $ctrl. Eg {{$ctrl.email}}

Contact Form template

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div class="form-wrapper">

    <form>
    <p>
        <label$gt;Email</label>
        <input type=“text” name=“email” ng-model="$ctrl.email" />
    </p>
    <p>
        label>Name</label>
        <input type=“text” name=“name” ng-model=“$ctrl.name" />
    </p>
    <p>
        <label>Surname</label>
        <input type=“text” name=“surname” ng-model=“$ctrl.surname” />
    </p>
    </form>

</div>