Components in Angular have a well-defined life cycle, which allows us to hook into different phases of it and have further control over our application. We can do this by implementing specific methods in the component's controller. In order to be more explicit, thanks to the expressiveness of TypeScript, we can implement different interfaces associated with the life cycle's phases. Each of these interfaces has a single method, which is associated with the phase itself.
Although code written with explicit interface implementation will have better semantics, since Angular supports ES5 as well, within the component we can simply define methods with the same names as the life cycle hooks (but this time, prefixed with ng
) and take advantage of duck typing.
The following diagram shows all the phases we can hook into:
Figure 10
Let's take a look at the different life cycle hooks:
OnChanges
: This hook will be invoked once a change in the input properties of a given component is detected. For instance, let's take a look at the following component:@Component({ selector: 'panel', inputs: ['title'] }) class Panel {...}
We can use it like this:
<panel [title]="expression"></panel>
Once the value of the expression associated with the [title]
attribute is changed, the ngOnChanges
hook will be invoked. We can implement it using this code snippet:
@Component(...) class Panel { ngOnChanges(changes) { Object.keys(changes).forEach(prop => { console.log(prop, 'changed. Previous value', changes[prop].previousValue); }); } }
The preceding snippet will display all the changed bindings and their old values. In order to be more explicit in the implementation of the hook, we can use interfaces:
import {Component, OnChanges} from '@angular/core'; @Component(...) class Panel implements OnChanges { ngOnChanges(changes) {...} }
All the interfaces representing the individual life cycle hooks declare a single method with the name of the interface itself prefixed with ng
. In the upcoming list, we'll use the term life cycle hook, both for interface and/or the method, except if we won't imply anything specifically for only one of them.
OnInit
: This hook will be invoked once the given component is initialized. We can implement it using the OnInit
interface with its ngOnInit
method.DoCheck
: This will be invoked when the change detector of the given component is invoked. It allows us to implement our own change detection algorithm for the given component. Note that DoCheck
and OnChanges
should not be implemented together on the same directive.OnDestroy
: If we implement the OnDestroy
interface with its ngOnDestroy
method, we can hook into the destroy life cycle phase of a component. This method will be invoked once the component is detached from the component tree.Now, let's take a look at the life cycle hooks associated with the component's content and view children:
AfterContentInit
: If we implement the ngAfterContentInit
life cycle hook, we will be notified when the component's content is fully initialized. This is the phase when the properties decorated with ContentChild
or ContentChildren
will be initialized.AfterContentChecked
: By implementing this hook, we'll be notified each time the content of the given component has been checked by the change detection mechanism of Angular.AfterViewInit
: If we implement this life cycle hook with its ngAfterViewInit
method, we will be notified when the component's view is initialized. This is the phase when the properties decorated with ViewChild
or ViewChildren
will be initialized.AfterViewChecked
: This is similar to AfterContentChecked
. The AfterViewChecked
hook will be invoked once the view of our component is checked.In order to trace the order of execution of the callbacks associated with each hook, let's take a peek at the ch4/ts/life-cycle/app.ts
example:
@Component({ selector: 'panel', template: '<ng-content></ng-content>' }) class Panel { @Input() title: string; @Input() caption: string; ngOnChanges(changes) {...} ngOnInit() {...} ngDoCheck() {...} ngOnDestroy() {...} ngAfterContentInit() {...} ngAfterContentChecked() {...} ngAfterViewInit() {...} ngAfterViewChecked() {...} }
The Panel
component implements all the hooks without explicitly implementing the interfaces associated with them.
We can use the component in the following template:
<button (click)="toggle()">Toggle</button> <div *ngIf="counter % 2 == 0"> <panel caption="Sample caption" >Hello world!</panel> </div>
In the preceding example, we have a panel and a button. Upon each click of the button, the panel will be either removed or appended to the view by the ngIf
directive.
During the application initialization, if the result of the "counter % 2 == 0"
expression is evaluated to true
, the ngOnChanges
method will be invoked. This happens because the values of the title and caption properties will be set for the first time.
Right after this, the ngOnInit
method will be called, since the component has been initialized. Once the component's initialization is completed, the change detection will be triggered, which will lead to the invocation of the ngDoCheck
method that allows us to hook custom logic for detecting changes in the state.
After the ngDoCheck
method, the change detector will perform a check on the component's content (ngAfterContentInit
and ngAfterContentChecked
will be invoked in this order). Right after this, the same will happen for the component's view (ngAfterViewInit
followed by ngAfterViewChecked
).
Once the expression of the ngIf
directive is evaluated to false
, the entire component will be detached from the view, which will lead to the invocation of the ngOnDestroy
hook.
On the next click, if the value of the expression of ngIf
is equal to true
, the same sequence of calls of the life cycle hooks as the one during the initialization phase will be executed.