The component() helper method shipped with so many great features to take us even closer towards Angular (v2+) syntax and integration. Let’s explore the $onInit method and the new require property’s syntax that makes the component() method much more powerful. If you’ve not checked out the component() method just yet, check my write-up on it here.
Table of contents
$onInit
Finally, the much needed callback when a component is mounted and ready - $onInit! It’s easy to use and is part of the component’s controller. See this example for usage:
angular
.module('app', [])
.component('parentComponent', {
transclude: true,
template: `
<div ng-transclude></div>
`,
})
.component('childComponent', {
bindings: {
count: '='
},
controller: function () {
this.state = 'Not loaded';
this.$onInit = function() {
this.state = 'Loaded!';
};
},
template: `
<div>
Component: {{ $ctrl.state }}
</div>
`
});
And a live example:
Angular has the ngOnInit method, a great lifecycle callback that will help us transition from AngularJS 1.x. ngOnInit is called right after the directive’s data-bound properties have been checked for the first time, and before any of its children have been checked. It is invoked only once when the directive is instantiated - much like in this AngularJS 1.x implementation.
Using “require” as an Object
Previously with Directives we used “require” to inherit methods from another Directive. This syntax was a simple String or Array, for example:
angular
.module('app', [])
.directive('parentComponent', function () {
scope: {},
require: ['^parentDirective', 'ngModel'],
controller: function () {
// controller logic
}
link: function ($scope, $element, $attrs, $ctrl) {
// $ctrl[0] === ^parentDirective
// $ctrl[1] === ^ngModel
},
template: `
<div>
Component: {{ $ctrl.state }}
</div>
`
});
Note how in the above example the require property is an Array, which is horribly passed to the link function as $ctrl, where we can access the Array index such as $ctrl[0] to get specific Controllers that we need.
Free eBook
Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.
-
Observables and Async Pipe -
Identity Checking and Performance -
Web Components <ng-template> syntax -
<ng-container> and Observable Composition -
Advanced Rendering Patterns -
Setters and Getters for Styles and Class Bindings
Thankfully, this is much nicer inside the component() method.
First, let’s add a method to the parentComponent Controller and a simple ng-transclude to pass a child Component into:
angular
.module('app', [])
.component('parentComponent', {
transclude: true,
template: `
<div ng-transclude></div>
`,
controller: function () {
this.foo = function () {
return 'Foo from parent!';
};
}
});
Then, we add the childComponent and declare require as an empty Object, empty Controller and placeholder {{ $ctrl.state }} inside the template:
angular
.module('app', [])
.component('parentComponent', {
transclude: true,
template: `
<div ng-transclude></div>
`,
controller: function () {
this.foo = function () {
return 'Foo from parent!';
};
}
})
.component('childComponent', {
require: {},
controller: function () {
},
template: `
<div>
Component! {{ $ctrl.state }}
</div>
`
});
Next up, a nice syntax change, the require property is an Object, not a String|Array as we saw in Directives!
angular
.module('app', [])
...
.component('childComponent', {
require: {
parent: '^parentComponent'
},
...
});
This allows us to now use this.parent as an inherited reference inside the childComponent’s Controller:
angular
.module('app', [])
...
.component('childComponent', {
require: {
parent: '^parentComponent'
},
controller: function () {
this.parent.foo();
},
...
});
But wait, this will throw an Error - it’s trying to call this.parent.foo(); before the Component is even ready. So let’s use the $onInit method that we just learned to be able to call it when it’s mounted:
angular
.module('app', [])
...
.component('childComponent', {
require: {
parent: '^parentComponent'
},
controller: function () {
this.$onInit = function () {
this.parent.foo(); // 'Foo from parent!'
};
},
...
});
Let’s now bind this.state inside childComponent, and assign the result of the inherited this.parent.foo(); call:
angular
.module('app', [])
...
.component('childComponent', {
require: {
parent: '^parentComponent'
},
controller: function () {
this.$onInit = function () {
this.state = this.parent.foo();
};
},
template: `
<div>
Component! {{ $ctrl.state }}
</div>
`
});
Altogether now:
angular
.module('app', [])
.component('parentComponent', {
transclude: true,
template: `
<div ng-transclude></div>
`,
controller: function () {
this.foo = function () {
return 'Foo from parent!';
};
}
})
.component('childComponent', {
require: {
parent: '^parentComponent'
},
controller: function () {
this.$onInit = function () {
this.state = this.parent.foo();
};
},
template: `
<div>
Component! {{ $ctrl.state }}
</div>
`
});
document.addEventListener('DOMContentLoaded', function () {
angular.bootstrap(document, ['app']);
});
Thank you for reading!