Last updated: November 15, 2020 8:44 PM
Angular 8 Demo Project Log and Experience Part 2.
Lifecycle Hooks
The general Angular life cycle is below:
This life cycle has different hooks:
constructor()
: This will always be called at first.ngOnChanges(changes: SimpleChanges): void {}
: Check changes of the component.- Called before any other lifecycle hook. Use it to inject dependencies, but avoid any serious work here.
- Called when input properties of component changes. changes is a dict type object
(key: property name, value: SimpleChange)
. - Add implements
OnChanges
to the class.
ngOnInit()
: Initialize the component. In this func we can safely use properties and methods of component.ngAfterContentInit(): void {}
: Initialize the content of component.- Called after
ngOnInit
when the component’s or directive’s content has been initialized. - Add implements
AfterContentInit
to the class. - By default, our component doesn’t support nesting. That is in the below example the Hello will not be displayed in the view. To display it, we add an
<ng-content>
tag in theScrollableTabComponent
to let the component have a content.
- Called after
ngAfterContentChecked(): void {}
: Check invalid data in the content of component. Called after every check of the component’s or directive’s content.ngAfterViewInit()
: Initialize the view of the component.- View includes the component and all its sub-components.
- Called after ngAfterContentInit when the component’s view has been initialized. Applies to components only.
ngAfterViewChecked(): void {}
: Check invalid data in the view. Called after every check of the component’s view. Applies to components only.ngOnCheck(): void {}
: Check invalid data of component.- Called every time that the input properties of a component or a directive are checked.
- Use it to extend change detection by performing a custom check.
ngOnDestroy(): void {}
: Called once, before the instance is destroyed. How to trigger this directive. We can use*ngIf
, if it is falsy the component will be destroyed. For example, This is used when we want to do some clearance, e.g. when setting interval.
Code example and output
Add the code in ScrollableTabComponent
constructor() {
console.log('constructor called');
}
ngOnChanges(changes: SimpleChanges): void {
console.log('Component input changes, call ngOnChanges: ', changes);
}
ngOnInit() {
console.log('ngOnInit called');
}
ngAfterContentInit(): void {
console.log('ngAfterContentInit called');
}
ngAfterContentChecked(): void {
console.log('ngAfterContentChecked called');
}
ngAfterViewInit(): void {
console.log('ngAfterViewInit called');
}
ngAfterViewChecked(): void {
console.log('ngAfterViewChecked called');
}
ngOnDestroy(): void {
console.log('ngOnDestroy');
}
The output is:
Template reference variables
Template reference variables I
We can use #refName
to name the reference of template element. For example,
<div #helloDiv>Hello</div>
Since the scope of the refName
is global, so it should be unique. Then we can use this refName
as the reference of template element. In the following example, the @ViewChild
is a selector, used to find the DOM element or component. ElementRef is a wrapper class of DOM element. Since DOM element is not an Angular class, so a wrapper class is needed to conveniently use and identify different types of DOM elements.
export class AppComponent {
@ViewChild('helloDiv') helloDivRef: ElementRef;
}
Template reference variables II
we can also use type name
. For example,
<app-image-slider [sliders]="imageSliders"></app-image-slider>
@ViewChild('ImageSliderComponent') imageSlider: ImageSliderComponent;
If we want to refer to multiple elements, we can use @ViewChildren(refName or typeName)
. Then declaration type should be QueryList<?>
.For example,
<img
#img
*ngFor="let slider of sliders"
[src]="slider.imgUrl"
[alt]="slider.caption"
/>
@ViewChildren('img') imgs: QueryList<ElementRef>;
Then we can change style of DOM elements. Changing properties of DOM elements is always not recommended in Angular, since this may cause Injection. Instead we can use Renderer2
module. For example,
constructor (private rd2: Renderer2) {}
@ViewChildren('img') imgs: QueryList<ElementRef>;
ngAfterViewInit () {
this.imgs.forEach(item => {
this.rd2.setStyle(item.nativeElement, 'height', '100px')
});
}
Summary
@ViewChild()
can be used to ref the view elements. These elements can be Angular component or DOM elements.- In AfterViewInit, we can safely use elements referred by
@ViewChild()
. - It’s better to use
Renderer2
to operate DOM elements.
Carousel: ImageSliderComponent
Use setInterval()
method, letting each img scrollLeft a specific length every 2 seconds. We did this in ngAfterViewInit()
.
Use Renderer2
to safely change the property, DO NOT use DOM directly.
export interface ImageSlider {
imgUrl: string;
link: string;
caption: string;
}
export class ImageSliderComponent implements OnInit, AfterViewInit, OnDestroy {
@Input() sliders: ImageSlider[] = [];
@Input() sliderHeight = '160xp';
@Input() intervalBySeconds = 2;
selectIndex = 0;
intervalId;
@ViewChild('imageSlider', { static: true }) imgSlider: ElementRef;
constructor(private rd2: Renderer2) {}
ngOnInit() {}
ngAfterViewInit(): void {
this.intervalId = setInterval(() => {
this.rd2.setProperty(
this.imgSlider.nativeElement,
'scrollLeft',
(this.getIndex(++this.selectIndex) *
this.imgSlider.nativeElement.scrollWidth) /
this.sliders.length
);
}, this.intervalBySeconds * 1000);
}
getIndex(idx: number): number {
return idx >= 0
? idx % this.sliders.length
: this.sliders.length - (Math.abs(idx) % this.sliders.length);
}
handleScroll(ev: any) {
const ratio =
(ev.target.scrollLeft * this.sliders.length) / ev.target.scrollWidth;
this.selectIndex = Math.round(ratio);
}
ngOnDestroy(): void {
clearInterval(this.intervalId);
}
}
<div class="container" [ngStyle]="{ height: sliderHeight }">
<div class="image-slider" (scroll)="handleScroll($event)" #imageSlider>
<img
#img
*ngFor="let slider of sliders"
[src]="slider.imgUrl"
[alt]="slider.caption"
/>
</div>
<div class="nav-section">
<span
*ngFor="let _ of sliders; let idx = index"
[ngClass]="{ 'slider-button-select': idx === selectIndex }"
class="slider-button"
>
</span>
</div>
</div>
.nav-section {
position: absolute;
bottom: 0;
width: 100%;
opacity: 0.5;
color: #fff;
background-color: #000000;
display: flex;
justify-content: flex-end;
align-items: stretch;
}
.nav-section .slider-button {
display: flex;
width: 10px;
height: 10px;
background-color: #fff;
text-decoration: none;
border-radius: 50%;
margin: 5px;
}
Call child component from parent component.
<app-image-slider [sliders]="imagesSliders" #imageSlider></app-image-slider>
The Reference name #imageSlider
same as in the ts file @ViewChild
.
We can safely use @ViewChild
referenced element in ngAfterViewInit
.
@ViewChild('imageSlider', { static: true }) imgSlider: ImageSliderComponent;
// @ViewChild(ImageSliderComponent) imgSlider: ImageSliderComponent;
imagesSliders: ImageSlider[] = [
{
imgUrl:
'https://xxx',
link: '',
caption: ''
},
]