diff --git a/cms/articles/1473861890/_article-1473861890.component.js b/cms/articles/1473861890/_article-1473861890.component.js new file mode 100644 index 0000000..840a991 --- /dev/null +++ b/cms/articles/1473861890/_article-1473861890.component.js @@ -0,0 +1,341 @@ +import { Component } from '@angular/core'; +import { DomSanitizer } from '@angular/platform-browser'; +import highlight from 'highlight.js'; + +import { xblogTableContentService } from 'xblog-cores/modules'; +import { resourceUtils } from 'xblog-cores/utils'; + + +export var article1473861890Component = Component({ + selector: 'article', + templateUrl: './templates/article-1473861890.html', + host: { + '[class.xblog-article-1473861890]': 'true' + } +}) +.Class({ + constructor: [ + DomSanitizer, + xblogTableContentService, + + function (sanitizer, tableContentService){ + this.id = 1473861890; + this.sanitizer = sanitizer; + this.tableContentService = tableContentService; + } + ], + + ngOnInit: function() { + this.tableContents = this.tableContentService + .getBuilder() + .addHeadings([ + { id: 'ngOnChanges', name: 'ngOnChanges' }, + { id: 'ngOnInit', name: 'ngOnInit' }, + { id: 'ngDoCheck', name: 'ngDoCheck' }, + { id: 'ngAfterContent', name: 'ngAfterContent' }, + { id: 'ngAfterView', name: 'ngAfterView' }, + { id: 'ngOnDestroy', name: 'ngOnDestroy' } + ]) + .build(); + + this.ngOnChanges = { + sourceCode: { + exampleComponent: { + name: 'ngOnChanges', + link: resourceUtils.getGithubArticleFileLink(this.id, 'ng-on-changes/example.component.js') + } + }, + screenCaptures: { + 1: resourceUtils.getImg('ngOnChanges-example-1473861890.png') + }, + codeBlocks: { + 1: this.getCodeBlock(getNgOnChanges) + } + }; + + this.ngOnInit = { + sourceCode: { + childComponent: { + name: 'ngOnInit', + link: resourceUtils.getGithubArticleFileLink(this.id, 'ng-on-init/child.component.js') + } + }, + screenCaptures: { + 1: resourceUtils.getImg('ngOnInit-example-1473861890.png') + }, + codeBlocks: { + 1: this.getCodeBlock(getNgOnInit) + } + }; + + this.ngDoCheck = { + sourceCode: { + childComponent: { + name: 'ngDoCheck', + link: resourceUtils.getGithubArticleFileLink(this.id, 'ng-do-check/child.component.js') + } + }, + screenCaptures: { + 1: resourceUtils.getImg('ngDoCheck-example-1473861890.png') + }, + codeBlocks: { + 1: this.getCodeBlock(getNgDoCheck) + } + }; + + this.ngAfterContent = { + sourceCode: { + childComponent: { + name: 'ngAfterContent', + link: resourceUtils.getGithubArticleFileLink(this.id, 'ng-after-content/child.component.js') + }, + exampleComponent: { + name: 'example.component.js', + link: resourceUtils.getGithubArticleFileLink(this.id, 'ng-after-content/example.component.js') + } + }, + screenCaptures: { + 1: resourceUtils.getImg('ngAfterContent-example-1473861890.png') + }, + codeBlocks: { + 1: this.getCodeBlock(getNgAfterContent01), + 2: this.getCodeBlock(getNgAfterContent02) + } + }; + + this.ngAfterView = { + sourceCode: { + exampleComponent: { + name: 'ngAfterView', + link: resourceUtils.getGithubArticleFileLink(this.id, 'ng-after-view/example.component.js') + } + }, + screenCaptures: { + 1: resourceUtils.getImg('ngAfterView-example-1473861890.png') + }, + codeBlocks: { + 1: this.getCodeBlock(getNgAfterView) + } + }; + }, + + getCodeBlock: function(getter, lang) { + var _langs = lang ? [ lang ] : ['javascript', 'html', 'css']; + + var _codeBlock = highlight.highlightAuto(getter().replace('\n', '').replace(/^ /gm, ''), _langs).value; + + return this.sanitizer.bypassSecurityTrustHtml(_codeBlock); + } +}); + +function getNgOnChanges(){ + return ` + import * as ngCore from '@angular/core'; + + import { childComponent } from './child.component'; + + export var ngOnChangesComponent = ngCore.Component({ + selector: 'my-example', + template: '', + directives: [ childComponent ] + }) + .Class({ + constructor: function(){}, + + ngOnInit: function() { + this.title = 'ngOnChanges'; + this.model = { content: 'This is content of ngOnChanges' }; + + var _self = this; + setTimeout(function(){ + _self.title = 'ngOnChanges updated'; + _self.model.content = 'This is content of ngOnChanges'; + }, 3 * 1000); + } + });`; +} + +function getNgOnInit(){ + return ` + import * as ngCore from '@angular/core'; + + export var childComponent = ngCore.Component({ + selector: 'my-child', + template: [ + '

{{title}}

', + '

{{model.content}}

' + ].join(''), + properties: ['title', 'model'] + }) + .Class({ + constructor: function(){ + console.log(this.title); + console.log(this.model); + }, + + ngOnInit: function(){ + console.log(this.title); + console.log(this.model); + } + });`; +} + +function getNgDoCheck(){ + return ` + import * as ngCore from '@angular/core'; + + export var childComponent = ngCore.Component({ + selector: 'my-child', + template: [ + '
', + '

ngDoCheck Example

', + 'Title: ', + '
' + ].join(''), + properties: ['title'] + }) + .Class({ + constructor: function(){}, + + ngOnInit: function(){ + this.model = { }; + this.model.title = this.currentTitle = this.title; + this.noChangeCount = 0; + this.changeDetected = null; + }, + + ngDoCheck: function(){ + if (this.model.title !== this.currentTitle) { + this.changeDetected = true; + + console.log('ngDoCheck: Title changed to ' + this.model.title + ' from ' + this.currentTitle); + + this.currentTitle = this.model.title; + } + + if (this.changeDetected) { this.noChangeCount = 0; } + else if (this.changeDetected === false) { + this.noChangeCount++; + console.log('ngDoCheck: called ' + this.noChangeCount + 'x when no change to title'); + } + + this.changeDetected = false; + } + });`; +} + +function getNgAfterContent01(){ + return ` + import * as ngCore from '@angular/core'; + + import { titleComponent } from './title.component'; + import { itemComponent } from './item.component'; + + export var childComponent = ngCore.Component({ + selector: 'my-child', + template: '', + queries: { + title: new ngCore.ContentChild(titleComponent), + items: new ngCore.ContentChildren(itemComponent) + } + }) + .Class({ + constructor: function(){}, + + ngAfterContentInit: function(){ + //title & items will be available since there + console.log('title', this.title); + console.log('items', this.items); + }, + + ngAfterContentChecked: function(){ + // If there're changes to contentChild + // contentChild is updated at there after the content has been checked + // This event is fired after every check of projected component content + } + });`; +} + +function getNgAfterContent02(){ + return ` + import * as ngCore from '@angular/core'; + + import { childComponent } from './child.component'; + import { titleComponent } from './title.component'; + import { itemComponent } from './item.component'; + + export var ngAfterContentComponent = ngCore.Component({ + selector: 'my-example', + template: [ + '', + '', + '', + '' + ].join(''), + directives: [ + childComponent, + titleComponent, + itemComponent + ] + }) + .Class({ + constructor: function(){}, + + ngOnInit: function() { + this.title = 'ngAfterContent'; + this.items = [ + { id: 1, name: 'item 01' }, + { id: 2, name: 'item 02' } + ]; + } + });`; +} + +function getNgAfterView(){ + return ` + import * as ngCore from '@angular/core'; + + import { childComponent } from './child.component'; + + export var ngAfterViewComponent = ngCore.Component({ + selector: 'my-example', + template: [ + '

', + '', + '
ngAfterViewChecked is fired: {{noChangeCount}}x
' + ].join(''), + directives: [ childComponent ], + queries: { + title: new ngCore.ViewChild('title', { read: ngCore.ElementRef }), + childs: new ngCore.ViewChildren(childComponent) + } + }) + .Class({ + constructor: function(){}, + + ngOnInit: function() { + this.items = [ + { id: 1, name: 'item 01' }, + { id: 2, name: 'item 02' } + ]; + this.noChangeCount = 0; + }, + + ngAfterViewInit: function(){ + //title & childs will be available since there + this.title.nativeElement.innerHTML = 'ngAfterView'; + console.log('title', this.title.nativeElement); + console.log('childs', this.items); + }, + + ngAfterViewChecked: function(){ + // If there're changes to ViewChild + // ViewChild is updated at there after the view has been checked + // This event is fired after every check of the component's views and child views. + var _self = this; + setTimeout(function(){ + if(!_self.noChangeCount){ _self.noChangeCount++; } + }); + } + });`; +} \ No newline at end of file diff --git a/cms/articles/1473861890/article-1473861890.component.js b/cms/articles/1473861890/article-1473861890.component.js new file mode 100644 index 0000000..c5852a6 --- /dev/null +++ b/cms/articles/1473861890/article-1473861890.component.js @@ -0,0 +1,341 @@ +import { Component } from '@angular/core'; +import { DomSanitizer } from '@angular/platform-browser'; +import highlight from 'highlight.js'; + +import { xblogTableContentService } from 'xblog-cores/modules'; +import { resourceUtils } from 'xblog-cores/utils'; + + +export var article1473861890Component = Component({ + selector: 'article', + template: "

In previous articles, you've alreadry gotten knowledges about directives/components.

Go on with a subject relate to them, I'll show you their lifecycle.


A component has a lifecycle managed by Angular itself.

Angular offers component lifecycle hooks that give us the ability to act when they occur.

No directive or component will implement all of them and some of the hooks only make sense for components.


After component's constructor is called to create an instance, Angular calls its lifecycle hook methods in following sequence.

Angular only calls a hook method if it is defined.


HooksTimingngOnChangesBefore ngOnInit and when a data-bound input property value changes.ngOnInitAfter the first ngOnChanges.ngDoCheckDuring every Angular change detection cycle.ngAfterContentInitAfter projecting content into the component.ngAfterContentCheckedAfter every check of projected component content.ngAfterViewInitAfter initializing the component's views and child views.ngAfterViewCheckedAfter every check of the component's views and child views.ngOnDestroyJust before Angular destroys the directive/component.

We'll detail each lifecycle hook and find out how we should work with them.


Table of Contents

ngOnChanges

This hook method executes before ngOnInit and when a data-bound input property value changes.

The method receives a object of current and previous values, I call it is changeRecord.


For example, I have ngOnChangesComponent as below.


{{ngOnChanges.sourceCode.exampleComponent.name}}Full Code

There're 2 binding, title is a primitive value, model is an object.

Because I bound data to childComponent so after its constructor executed, its ngOnChanges will be fired.

3 seconds later, its ngOnChanges fires one more time because I've changed data-bound property values.

As you see the below image, although ngOnChanges fires twice, but its changeRecord is not same.

In the second changes, changeRecord only contains current and previous values of title.

The value of model property is the reference to the object. Angular doesn't care if any its property's value changed.

If you really want Angular to fire ngOnChanges for every changes of model. Immutable will be a solution for you.



ngOnInit

This hook method executes after the first ngOnChanges.

We turn to ngOnInit for 2 reasons:


- To perform complex initializations shortly after construction.

- To set up the component after Angular sets the input properties.


Why shouldn't we fetch data from server in the constructor ? Because data-bound input properties only are set after construction.

If you fetch data with params which base on values of input properties, make sure you will get errors.

The constructor should do no more than set the initial local variables to simple values.


Look at the example with childComponent.


{{ngOnInit.sourceCode.childComponent.name}}Full Code

And this is a screen capture for result.



ngDoCheck

It is called with enormous frequency, Angular fires it after every change detection cycle no matter where the change occurred.

Look at below sample, when you type something into textbox ngDoCheck will be called.

Even, you move out of textbox, it's also called.


{{ngDoCheck.sourceCode.childComponent.name}}Full Code


Clearly, if you decide to implement your logic in there, make sure implementation must be very lightweight or the user experience may suffer.


ngAfterContent

The ngAfterContentInit and ngAfterContentChecked hooks that Angular calls after Angular projects external content into the component.

Angular calls both ngAfterContent hooks before calling either of the ngAfterView hooks.


The hooks take action based on changing values in a content child which we can only reach by querying for it via ContentChild.

The ngAfterContent hooks concern ContentChildren, the child components that Angular projected into the component.


Using ng-content is a way to import Html from outside the component and insert content into component's template in a designated spot.


I have an exemple with childComponent which use ng-content like this.


{{ngAfterContent.sourceCode.childComponent.name}}Full Code

In exampleComponent I've' projected titleComponent and itemComponent into childComponent like this.


{{ngAfterContent.sourceCode.exampleComponent.name}}Full Code

So you can see result in the below image, I've queried instances of titleComponent & itemComponent.



Notice that, Angular completes composition of the projected content before finishing the composition of this component's view.

We still have a window of opportunity to modify that view.


ngAfterView

The ngAfterViewInit and ngAfterViewChecked execute after initializing the component's views and child views.


The hooks take action based on changing values within the child view which we can only reach by querying for the child view via ViewChild.

The AfterView hooks concern ViewChildren, the child components whose element tags appear within the component's template.


Have a look at exampleComponent & the result below.


{{ngAfterView.sourceCode.exampleComponent.name}}Full Code


Do you see I used setTimeout() in the example ?

If you try to update component's data-bound property immediately instead of using setTimeout, Angular throws an error.


Notice that we must adhere to Angular's unidirectional data flow rule which says that we may not update the view after it has been composed.

Both hooks fire after the component's view has been composed.


For ngAfterViewChecked, Angular frequently calls it, even when there are no changes of interest. Write lean hook methods to avoid performance problems.


ngOnDestroy

This hook run just before Angular destroys the directive/component.

This is the place to free resources that won't be garbage collected automatically such as unsubscribe from observables and DOM events, stop interval timers etc.


", + host: { + '[class.xblog-article-1473861890]': 'true' + } +}) +.Class({ + constructor: [ + DomSanitizer, + xblogTableContentService, + + function (sanitizer, tableContentService){ + this.id = 1473861890; + this.sanitizer = sanitizer; + this.tableContentService = tableContentService; + } + ], + + ngOnInit: function() { + this.tableContents = this.tableContentService + .getBuilder() + .addHeadings([ + { id: 'ngOnChanges', name: 'ngOnChanges' }, + { id: 'ngOnInit', name: 'ngOnInit' }, + { id: 'ngDoCheck', name: 'ngDoCheck' }, + { id: 'ngAfterContent', name: 'ngAfterContent' }, + { id: 'ngAfterView', name: 'ngAfterView' }, + { id: 'ngOnDestroy', name: 'ngOnDestroy' } + ]) + .build(); + + this.ngOnChanges = { + sourceCode: { + exampleComponent: { + name: 'ngOnChanges', + link: resourceUtils.getGithubArticleFileLink(this.id, 'ng-on-changes/example.component.js') + } + }, + screenCaptures: { + 1: resourceUtils.getImg('ngOnChanges-example-1473861890.png') + }, + codeBlocks: { + 1: this.getCodeBlock(getNgOnChanges) + } + }; + + this.ngOnInit = { + sourceCode: { + childComponent: { + name: 'ngOnInit', + link: resourceUtils.getGithubArticleFileLink(this.id, 'ng-on-init/child.component.js') + } + }, + screenCaptures: { + 1: resourceUtils.getImg('ngOnInit-example-1473861890.png') + }, + codeBlocks: { + 1: this.getCodeBlock(getNgOnInit) + } + }; + + this.ngDoCheck = { + sourceCode: { + childComponent: { + name: 'ngDoCheck', + link: resourceUtils.getGithubArticleFileLink(this.id, 'ng-do-check/child.component.js') + } + }, + screenCaptures: { + 1: resourceUtils.getImg('ngDoCheck-example-1473861890.png') + }, + codeBlocks: { + 1: this.getCodeBlock(getNgDoCheck) + } + }; + + this.ngAfterContent = { + sourceCode: { + childComponent: { + name: 'ngAfterContent', + link: resourceUtils.getGithubArticleFileLink(this.id, 'ng-after-content/child.component.js') + }, + exampleComponent: { + name: 'example.component.js', + link: resourceUtils.getGithubArticleFileLink(this.id, 'ng-after-content/example.component.js') + } + }, + screenCaptures: { + 1: resourceUtils.getImg('ngAfterContent-example-1473861890.png') + }, + codeBlocks: { + 1: this.getCodeBlock(getNgAfterContent01), + 2: this.getCodeBlock(getNgAfterContent02) + } + }; + + this.ngAfterView = { + sourceCode: { + exampleComponent: { + name: 'ngAfterView', + link: resourceUtils.getGithubArticleFileLink(this.id, 'ng-after-view/example.component.js') + } + }, + screenCaptures: { + 1: resourceUtils.getImg('ngAfterView-example-1473861890.png') + }, + codeBlocks: { + 1: this.getCodeBlock(getNgAfterView) + } + }; + }, + + getCodeBlock: function(getter, lang) { + var _langs = lang ? [ lang ] : ['javascript', 'html', 'css']; + + var _codeBlock = highlight.highlightAuto(getter().replace('\n', '').replace(/^ /gm, ''), _langs).value; + + return this.sanitizer.bypassSecurityTrustHtml(_codeBlock); + } +}); + +function getNgOnChanges(){ + return ` + import * as ngCore from '@angular/core'; + + import { childComponent } from './child.component'; + + export var ngOnChangesComponent = ngCore.Component({ + selector: 'my-example', + template: '', + directives: [ childComponent ] + }) + .Class({ + constructor: function(){}, + + ngOnInit: function() { + this.title = 'ngOnChanges'; + this.model = { content: 'This is content of ngOnChanges' }; + + var _self = this; + setTimeout(function(){ + _self.title = 'ngOnChanges updated'; + _self.model.content = 'This is content of ngOnChanges'; + }, 3 * 1000); + } + });`; +} + +function getNgOnInit(){ + return ` + import * as ngCore from '@angular/core'; + + export var childComponent = ngCore.Component({ + selector: 'my-child', + template: [ + '

{{title}}

', + '

{{model.content}}

' + ].join(''), + properties: ['title', 'model'] + }) + .Class({ + constructor: function(){ + console.log(this.title); + console.log(this.model); + }, + + ngOnInit: function(){ + console.log(this.title); + console.log(this.model); + } + });`; +} + +function getNgDoCheck(){ + return ` + import * as ngCore from '@angular/core'; + + export var childComponent = ngCore.Component({ + selector: 'my-child', + template: [ + '
', + '

ngDoCheck Example

', + 'Title: ', + '
' + ].join(''), + properties: ['title'] + }) + .Class({ + constructor: function(){}, + + ngOnInit: function(){ + this.model = { }; + this.model.title = this.currentTitle = this.title; + this.noChangeCount = 0; + this.changeDetected = null; + }, + + ngDoCheck: function(){ + if (this.model.title !== this.currentTitle) { + this.changeDetected = true; + + console.log('ngDoCheck: Title changed to ' + this.model.title + ' from ' + this.currentTitle); + + this.currentTitle = this.model.title; + } + + if (this.changeDetected) { this.noChangeCount = 0; } + else if (this.changeDetected === false) { + this.noChangeCount++; + console.log('ngDoCheck: called ' + this.noChangeCount + 'x when no change to title'); + } + + this.changeDetected = false; + } + });`; +} + +function getNgAfterContent01(){ + return ` + import * as ngCore from '@angular/core'; + + import { titleComponent } from './title.component'; + import { itemComponent } from './item.component'; + + export var childComponent = ngCore.Component({ + selector: 'my-child', + template: '', + queries: { + title: new ngCore.ContentChild(titleComponent), + items: new ngCore.ContentChildren(itemComponent) + } + }) + .Class({ + constructor: function(){}, + + ngAfterContentInit: function(){ + //title & items will be available since there + console.log('title', this.title); + console.log('items', this.items); + }, + + ngAfterContentChecked: function(){ + // If there're changes to contentChild + // contentChild is updated at there after the content has been checked + // This event is fired after every check of projected component content + } + });`; +} + +function getNgAfterContent02(){ + return ` + import * as ngCore from '@angular/core'; + + import { childComponent } from './child.component'; + import { titleComponent } from './title.component'; + import { itemComponent } from './item.component'; + + export var ngAfterContentComponent = ngCore.Component({ + selector: 'my-example', + template: [ + '', + '', + '', + '' + ].join(''), + directives: [ + childComponent, + titleComponent, + itemComponent + ] + }) + .Class({ + constructor: function(){}, + + ngOnInit: function() { + this.title = 'ngAfterContent'; + this.items = [ + { id: 1, name: 'item 01' }, + { id: 2, name: 'item 02' } + ]; + } + });`; +} + +function getNgAfterView(){ + return ` + import * as ngCore from '@angular/core'; + + import { childComponent } from './child.component'; + + export var ngAfterViewComponent = ngCore.Component({ + selector: 'my-example', + template: [ + '

', + '', + '
ngAfterViewChecked is fired: {{noChangeCount}}x
' + ].join(''), + directives: [ childComponent ], + queries: { + title: new ngCore.ViewChild('title', { read: ngCore.ElementRef }), + childs: new ngCore.ViewChildren(childComponent) + } + }) + .Class({ + constructor: function(){}, + + ngOnInit: function() { + this.items = [ + { id: 1, name: 'item 01' }, + { id: 2, name: 'item 02' } + ]; + this.noChangeCount = 0; + }, + + ngAfterViewInit: function(){ + //title & childs will be available since there + this.title.nativeElement.innerHTML = 'ngAfterView'; + console.log('title', this.title.nativeElement); + console.log('childs', this.items); + }, + + ngAfterViewChecked: function(){ + // If there're changes to ViewChild + // ViewChild is updated at there after the view has been checked + // This event is fired after every check of the component's views and child views. + var _self = this; + setTimeout(function(){ + if(!_self.noChangeCount){ _self.noChangeCount++; } + }); + } + });`; +} \ No newline at end of file diff --git a/cms/articles/1473861890/article-1473861890.component.prebuild.json b/cms/articles/1473861890/article-1473861890.component.prebuild.json new file mode 100644 index 0000000..84e75cb --- /dev/null +++ b/cms/articles/1473861890/article-1473861890.component.prebuild.json @@ -0,0 +1,4 @@ +{ + "origin": "./cms/articles/1473861890/_article-1473861890.component.js", + "inline": "./cms/articles/1473861890/article-1473861890.component.js" +} \ No newline at end of file diff --git a/cms/articles/1473861890/index.js b/cms/articles/1473861890/index.js new file mode 100644 index 0000000..5de551c --- /dev/null +++ b/cms/articles/1473861890/index.js @@ -0,0 +1,15 @@ +import { resourceUtils } from 'xblog-cores/utils'; +import { article1473861890Component } from './article-1473861890.component'; + +export var article1473861890 = { + id: 1473861890, + title: 'Component Lifecycle In Angular 2', + postedDate: 'Wed Sep 14 2016 21:04:49 GMT+0700 (SE Asia Standard Time)', + author: 'Minh Van', + cover: resourceUtils.getImg('xblog-home-cover.jpg'), + routeLink: resourceUtils.getArticleRouteLink('component-lifecycle-in-angular-2-1473861890.html'), + relatedArticles: [], + tags: [], + description: 'A component has a lifecycle managed by Angular itself. Angular offers component lifecycle hooks that give us the ability to act when they occur.', + content: article1473861890Component, +}; diff --git a/cms/articles/1473861890/resources/images/ngAfterContent-example-1473861890.png b/cms/articles/1473861890/resources/images/ngAfterContent-example-1473861890.png new file mode 100644 index 0000000..0bfbcb5 Binary files /dev/null and b/cms/articles/1473861890/resources/images/ngAfterContent-example-1473861890.png differ diff --git a/cms/articles/1473861890/resources/images/ngAfterView-example-1473861890.png b/cms/articles/1473861890/resources/images/ngAfterView-example-1473861890.png new file mode 100644 index 0000000..9d70c02 Binary files /dev/null and b/cms/articles/1473861890/resources/images/ngAfterView-example-1473861890.png differ diff --git a/cms/articles/1473861890/resources/images/ngDoCheck-example-1473861890.png b/cms/articles/1473861890/resources/images/ngDoCheck-example-1473861890.png new file mode 100644 index 0000000..1eb3601 Binary files /dev/null and b/cms/articles/1473861890/resources/images/ngDoCheck-example-1473861890.png differ diff --git a/cms/articles/1473861890/resources/images/ngOnChanges-example-1473861890.png b/cms/articles/1473861890/resources/images/ngOnChanges-example-1473861890.png new file mode 100644 index 0000000..2187f8f Binary files /dev/null and b/cms/articles/1473861890/resources/images/ngOnChanges-example-1473861890.png differ diff --git a/cms/articles/1473861890/resources/images/ngOnInit-example-1473861890.png b/cms/articles/1473861890/resources/images/ngOnInit-example-1473861890.png new file mode 100644 index 0000000..3a154f7 Binary files /dev/null and b/cms/articles/1473861890/resources/images/ngOnInit-example-1473861890.png differ diff --git a/cms/articles/1473861890/templates/article-1473861890.html b/cms/articles/1473861890/templates/article-1473861890.html new file mode 100644 index 0000000..454f22d --- /dev/null +++ b/cms/articles/1473861890/templates/article-1473861890.html @@ -0,0 +1,209 @@ +

In previous articles, you've alreadry gotten knowledges about directives/components.

+

Go on with a subject relate to them, I'll show you their lifecycle.

+
+

A component has a lifecycle managed by Angular itself.

+

Angular offers component lifecycle hooks that give us the ability to act when they occur.

+

No directive or component will implement all of them and some of the hooks only make sense for components.

+
+

After component's constructor is called to create an instance, Angular calls its lifecycle hook methods in following sequence.

+

Angular only calls a hook method if it is defined.

+
+ + + + Hooks + Timing + + + + ngOnChanges + + Before ngOnInit and when a data-bound input property value changes. + + + + + ngOnInit + + After the first ngOnChanges. + + + + + ngDoCheck + + During every Angular change detection cycle. + + + + + ngAfterContentInit + + After projecting content into the component. + + + + + ngAfterContentChecked + + After every check of projected component content. + + + + + ngAfterViewInit + + After initializing the component's views and child views. + + + + + ngAfterViewChecked + + After every check of the component's views and child views. + + + + + ngOnDestroy + + Just before Angular destroys the directive/component. + + + + +
+

We'll detail each lifecycle hook and find out how we should work with them.

+
+ + +

Table of Contents
+ + +
+

ngOnChanges

+

This hook method executes before ngOnInit and when a data-bound input property value changes.

+

The method receives a object of current and previous values, I call it is changeRecord.

+
+

For example, I have ngOnChangesComponent as below.

+
+ + {{ngOnChanges.sourceCode.exampleComponent.name}} + Full Code + + +
+
+

There're 2 binding, title is a primitive value, model is an object.

+

Because I bound data to childComponent so after its constructor executed, its ngOnChanges will be fired.

+

3 seconds later, its ngOnChanges fires one more time because I've changed data-bound property values.

+

As you see the below image, although ngOnChanges fires twice, but its changeRecord is not same.

+

In the second changes, changeRecord only contains current and previous values of title.

+

The value of model property is the reference to the object. Angular doesn't care if any its property's value changed.

+

If you really want Angular to fire ngOnChanges for every changes of model. Immutable will be a solution for you.

+
+
+
+

ngOnInit

+

This hook method executes after the first ngOnChanges.

+

We turn to ngOnInit for 2 reasons:

+
+

- To perform complex initializations shortly after construction.

+

- To set up the component after Angular sets the input properties.

+
+

Why shouldn't we fetch data from server in the constructor ? Because data-bound input properties only are set after construction.

+

If you fetch data with params which base on values of input properties, make sure you will get errors.

+

The constructor should do no more than set the initial local variables to simple values.

+
+

Look at the example with childComponent.

+
+ + {{ngOnInit.sourceCode.childComponent.name}} + Full Code + + +
+
+

And this is a screen capture for result.

+
+
+
+

ngDoCheck

+

It is called with enormous frequency, Angular fires it after every change detection cycle no matter where the change occurred.

+

Look at below sample, when you type something into textbox ngDoCheck will be called.

+

Even, you move out of textbox, it's also called.

+
+ + {{ngDoCheck.sourceCode.childComponent.name}} + Full Code + + +
+
+
+
+

Clearly, if you decide to implement your logic in there, make sure implementation must be very lightweight or the user experience may suffer.

+
+

ngAfterContent

+

The ngAfterContentInit and ngAfterContentChecked hooks that Angular calls after Angular projects external content into the component.

+

Angular calls both ngAfterContent hooks before calling either of the ngAfterView hooks.

+
+

The hooks take action based on changing values in a content child which we can only reach by querying for it via ContentChild.

+

The ngAfterContent hooks concern ContentChildren, the child components that Angular projected into the component.

+
+

Using ng-content is a way to import Html from outside the component and insert content into component's template in a designated spot.

+
+

I have an exemple with childComponent which use ng-content like this.

+
+ + {{ngAfterContent.sourceCode.childComponent.name}} + Full Code + + +
+
+

In exampleComponent I've' projected titleComponent and itemComponent into childComponent like this.

+
+ + {{ngAfterContent.sourceCode.exampleComponent.name}} + Full Code + + +
+
+

So you can see result in the below image, I've queried instances of titleComponent & itemComponent.

+
+
+
+

Notice that, Angular completes composition of the projected content before finishing the composition of this component's view.

+

We still have a window of opportunity to modify that view.

+
+

ngAfterView

+

The ngAfterViewInit and ngAfterViewChecked execute after initializing the component's views and child views.

+
+

The hooks take action based on changing values within the child view which we can only reach by querying for the child view via ViewChild.

+

The AfterView hooks concern ViewChildren, the child components whose element tags appear within the component's template.

+
+

Have a look at exampleComponent & the result below.

+
+ + {{ngAfterView.sourceCode.exampleComponent.name}} + Full Code + + +
+
+
+
+

Do you see I used setTimeout() in the example ?

+

If you try to update component's data-bound property immediately instead of using setTimeout, Angular throws an error.

+
+

Notice that we must adhere to Angular's unidirectional data flow rule which says that we may not update the view after it has been composed.

+

Both hooks fire after the component's view has been composed.

+
+

For ngAfterViewChecked, Angular frequently calls it, even when there are no changes of interest. Write lean hook methods to avoid performance problems.

+
+

ngOnDestroy

+

This hook run just before Angular destroys the directive/component.

+

This is the place to free resources that won't be garbage collected automatically such as unsubscribe from observables and DOM events, stop interval timers etc.

+
\ No newline at end of file diff --git a/cms/articles/index.js b/cms/articles/index.js index e1aafe3..1bc30a7 100644 --- a/cms/articles/index.js +++ b/cms/articles/index.js @@ -13,8 +13,11 @@ import { xblogTableContentService } from 'xblog-cores/modules'; -var _ARTICLES = []; +import { article1473861890 } from './1473861890'; +var _ARTICLES = [ + article1473861890 +]; export var ARTICLE_STORE = _init(); @@ -22,7 +25,6 @@ var _ARTICLE_COMPONENTS = ARTICLE_STORE.LIST.map(function(article){ return article.content; }); - export var cmsArticlesModuleMetadata = Class({ constructor: function cmsArticlesModuleMetadata(){ Object.assign(this, {