0

I have used a child component in my parent component. Child onchanges is triggered first time through ngoninit. But when i trigger onClick function , Child onchanges does not get triggered. What am I doing wrong?

<app-child [inputData]="data"></app-child>

ParentComponent :

{
    data = [];

    ngOnInit() 
    {
        this.data = [1,2]
    }

     onClick()
    {
        console.log("clicked");
        this.data = [3,4];
    }

}

ChildComponent:

@Input inputData;

ngOnChanges(changes:SimpleChanges) 
{   
    console.log("change detected xxxxxxx");
}

NOTE: Instead of this.data = [3,4], I also tried this.data.push(3), still no change

tryingToLearn
  • 10,691
  • 12
  • 80
  • 114

3 Answers3

2

The reason because you are not getting the catch in the ngOnChanges on your child's component is because you are changing the value in the data variable and not its reference.

Implementing ngDoCheck and IterableDiffers can be a solution, more details can be found here: Angular 2: How to detect changes in an array? (@input property)

Another way to solve this problem could be to use JSON.parse:

onClick() {
  this.data = JSON.parse(JSON.stringify([3,4]));
}

My personal opinion is to not use NgDoCheck since it's really heavy on memory. I would personally use a Subject in order to trigger the update of the child component.

Ideal service:

@Injectable()
export class YourService {
  private _$forceRender = new Subject<boolean>();
  public onForceRender = this._$forceRender.asObservable();

  public setForceRender(): void {
    this._$forceRender.next(true);
  }
} 

And then on your onClick function:

onClick() {
  console.log("clicked");
  this.data = [3,4];
  this.myService.setForceRender();
}

Finally in your child component:

  constructor(
     ...
     _cdr: ChangeDetectionRef,
    ...) { }

    ngOnInit(): void {
      this.myservice.onForceRender.subscribe(() => {
        this._cdr.detectChanges();
      });
    }

More details about ChangeDetection here

Jacopo Sciampi
  • 2,990
  • 1
  • 20
  • 44
  • even I change existing reference, e.g. `this.data.push()`, it didn't work in my case. – tryingToLearn Jun 23 '21 at 14:07
  • `push` method does change reference. It just add an element. – Jacopo Sciampi Jun 23 '21 at 14:10
  • I mean, as you mentioned "The reason because you are not getting the catch in the ngOnChanges on your child's component is because you are changing the value in the data variable and not its reference." , so changing the value in reference by push method should have worked , right ? – tryingToLearn Jun 23 '21 at 14:14
  • `push` method only mutates the array and no new reference is created, I've edited my answer with a third _method_ that can solve your problem. – Jacopo Sciampi Jun 23 '21 at 14:33
1

I would suggest you to make it in immutable way, so that you notify Angular's change detection as this is the easiest way. You can do it like that:

 onClick()
{
    console.log("clicked");
    this.data = [3,4];
    this.data = this.data.slice();
}

For further information, you can check immutable operations you can make with arrays.

Fatih Ersoy
  • 680
  • 1
  • 5
  • 24
1

Though both the answers provided good learning, but I fixed it by cloning existing array and calling detectChanges

this.data = Object.assign([], [3,4]);          
this.cdr.detectChanges();
tryingToLearn
  • 10,691
  • 12
  • 80
  • 114