haya
haya

Reputation: 5

angular shadow DOM, child component trigger ngOnChanges twice

angular shadow DOM, child component trigger ngOnChanges twice, and first time all input data are undefined. How to avoid the first trigger?

a.component, is Shadow DOM.

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>

b.component

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

Answers (1)

Misha Mashina
Misha Mashina

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

Related Questions