Welcome Angular 1.5 .component() method

Carlos Menezes
Frontend Weekly
Published in
4 min readFeb 17, 2016

The new method is supposed to help everyone migrate to Angular 2; they say “The primary theme for this release was to improve the upgrade path to Angular 2”, which will also allow the developers to write in an Angular 2 style. However, all is not as rosy as it first appears if we compare the .component() method directly to .directive() method. I will discuss this further on.

The new helper method was introduced to simplify the .directive() to apply the best practices and common default behaviours. In essence it is just a ‘wrapperaround directives with good defaults; essentially it just makes your code better and also helps moving your component to ngForward/Angular 2.0 much easier, because components are the building blocks of Angular 2 architecture. It aims to simplify the way we create “components” — which means UI directives.

The new Angular 1.5 version also comes with more features like:

  1. Lazy transclusion
  2. Multiple slot transclusion
  3. ng-animate-swap
  4. Component definition

Find out more here; in this article we are going to focus only on .component() definition method.

Component definition

Let’s compare the .component() method to the old directive:

// Angular 1.4
module.directive(name, fn);
module.directive('timePicker', function timePicker() {
return {

};
});
// Angular 1.5
module.component(name, options);
module.component('timePicker', {

});

This method is just a syntax sugar for the old .directive() method you already know and everything you do with .directive() you can do pretty much the same with .component(), but applying the best practices which are:

  1. The use of the controllers instead of link functions.
  2. Isolated scopes, bindToController and controllerAs by default.

How does the code look before and after?

Before — using .directive()

angular
.module('app')
.directive('card', usingTransclusion);
function usingTransclusion() {
return {
restrict: 'E',
transclude: true,
controller: function CardCtrl() {},
scope: {},
controllerAs: '$ctrl',
bindToController: { // now bindings
title: '@'
},
template: [
'<div>',
'<h3>{{$ctrl.title || "No title"}}</h3>',
'<i><ng-transclude>Empty</ng-transclude></i>',
'</div>'
].join('')
}
};

After - using .component()

angular
.module('app')
.component('card', {
transclude: true,
controller: function CardCtrl() {},
bindings: {
title: '@'
},
template: [
'<div>',
'<h3>{{$ctrl.title || "No title"}}</h3>',
'<i><ng-transclude>Empty</ng-transclude></i>',
'</div>'
].join('')
});

As you can see not much has changed but you have a much simpler and more straightforward method.

A few things to take into consideration in our example above using .component() method:

  1. We do not need to isolate the $scope any more because it does that automatically for us.
  2. Using bindings property we can define what we want to pass down to the component assuming that the component will have the isolate scope.
  3. We can still define controllerAs or controller:”YourCtrl as alias” (although I prefer that one) in our .component() method, but by default it assigns the default name for us ($ctrl) where we can use it directly in our template.
  4. We do not need to write a function that always return the same object, but now becomes the Object definition inside.

Here is the return method of Angular 1.5 .component() source code.

return {
controller: controller,
controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl',
template: makeInjectable(template),
templateUrl: makeInjectable(options.templateUrl),
transclude: options.transclude,
scope: {},
bindToController: options.bindings || {},
restrict: 'E',
require: options.require
};

You can also find out more, about the new component guide here and the Angular 1.5 source code here.

A quick example of the .directive() and .component() working together.

If you remember I began the article saying “all is not as rosy as it first appears” and…

When should we use the .component() method?

You cannot or should not use it when:

  1. You have to extend or manipulate the DOM, basically the link function.
  2. If you want a template-less directive. Example: ng-click!

Because the .component() saves on boilerplate and less-errors, it is good to use it if you are considering moving to Angular 2.

$onInit

This a new method that will be called from the compiler, when the component and all bindings are initialised. Example:

angular
.module('app', [])
.component('card', {
bindings: {
title: '='
},
controller: function () {
this.title= 'Not loaded';
this.$onInit = function() {
this.title= 'Loaded!';
};

},
template: `
<div>
Title: {{ $ctrl.title}}
</div>
`
});

If you have already played with Angular 2, you might know about the “ngOnInit” which is one of the components lifecycle of Angular 2 and using this method will also make transitioning components to ng2 easier.

The new “require” Object

The “require” object is one of the ways of communicating between .directive().

How it should look like when using the .component() method is as per the following example:

Before — using .directive()

    require: ['^parent', 'ngModel'],
....
});

After — using .component()

angular
.module('app', [])
.component('parent', {
transclude: true,
template: `
<div ng-transclude></div>
`,
controller: function () {
this.hiya= function () {
return 'Hiya from parent!';
};
}
})
.component('child', {
require: {
parent: '^parent'
},

controller: function () {
this.$onInit = function () {
this.parent.hiya(); // 'Hiya from parent!'
};

}
...

The syntax has changed and the require property is an Object, and not a String or Array as you can see when we use the .directive() and this allow us to use this.parent inside the child’s controller. If you do not use the method $onInit, it will throw back an error because the component has not been initialised.

One-way bindings

{
...
bindings: {
oneWay: '<',
twoWay: '='
},
...
}

It looks similar to one-time bindings {{::name}} when the angular team introduced it on 1.3 version but they are completely different! They create a single watch and we gain performance and lose errors. Once again a step towards the way things behave in ng2.

--

--

Carlos Menezes
Frontend Weekly

Full-stack & Software Engineer, Javascript, Angular, MVC, C# and all other web things ✌ ☃ London ☂ ☁ 🇸🇹 > 🇵🇹 > 🇪🇸 > 🇬🇧