Reputation: 5
angular shadow DOM, child component trigger ngOnChanges twice, and first time all input data are undefined. How to avoid the first trigger?
ts:
import { ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, OnInit, Output, SimpleChanges, ViewChild, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'app-a',
templateUrl: './a.component.html',
styleUrls: ['./a.component.scss'],
encapsulation: ViewEncapsulation.ShadowDom
})
export class ContainerComponent implements OnInit {
@Input() mode?: string;
@Input() size?: string;
@Input() fileList?: any;
constructor() {}
ngOnInit(): void {}
}
html:
<div #container>
<app-b
[mode]="mode"
[size]="size"
[fileList]="fileList">
</app-b>
</div>
ts:
import { ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, OnInit, Output, SimpleChanges, ViewChild, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'app-b',
templateUrl: './b.component.html',
styleUrls: ['./b.component.scss'],
})
export class ContainerComponent implements OnInit {
@Input() mode?: string = 'edit';
@Input() size?: string = 'm';
@Input() fileList?: any = [];
constructor() {}
ngOnInit(): void {}
ngOnChanges(changes: SimpleChanges) {
console.log(changes);
}
}
when I import a component named a, and use it like below, function of ngOnChanges will be trigger twice in b component. First time, get all input data are undefined, the second time get {size: 'g'}
; at the time, mode
and fileList
are both change to undefined
, not default value 'edit'
and []
.
<app-a size="g"></app-a>
I want to know how to avoid the first trigger?
Upvotes: 0
Views: 98
Reputation: 2113
First, ngOnChanges will be triggered before the B's (or any component's) onInit, and second time it will be called after B's onInit, i.e. when the binded data came from A. You cannot avoid the triggers - you can only choose wether you'll act upon changes or not. The fact that you can log changes doesn't mean you must act on them.
Second, the default value of an Input is not a "real" default value, I mean not in the sense in which you expect it to behave: it will have that initial value only if nothing is passed to Input i.e. if the value is not binded in parent component. However, your A component does bind values to B's Inputs, and the problem here is that the A itself is not getting those values passed to it. So when you only pass size
to A, it will get passed to B, but other Inputs will be undefined because their values are binded in A's template, but they have no values (nothing passed to A for them). So B's onChanges will fire before B's onInit the value undefined
for all Inputs, then B will get size
from A (because A got it from its parent) and so B's size
will be 'g'. However, nothing was passed to A for other Inputs and they stay undefined - so they're undefined in B too, since they are binded in A's template.
If you want to have 'real' default values, you can use setters and getters for Inputs or set the values in onInit.
Upvotes: 0