How to trigger a change event manually - angular2
Asked Answered
N

2

30

Given the following components:

@Component({
    selector: 'compA',
    template:  template: `<compB [item]=item></compB>`
})
export class CompA {
    item:any;
    updateItem():void {
        item[name] = "updated name";
    }
}

@Component({
    selector: 'compB',
    template:  template: `<p>{{item[name]}}</p>`
})
export class CompB implements OnInit{
    @Input() item: any;
    someArray: any[];

    ngOnInit():void {
        someArray.push("something");
    }
}

As far as I understood that unless the complete item object is changed, angular2 does not recognize the changes on item. Therefore, I'd like to emit a change event manually for item when the updateItem method is called. And afterwards, make the child component i.e. CompB re-rendered as if angular detected a change in the regular way.

Currently, what I have done is to implement the ngOnInit method of for CompB and call that method inside updateItem method through a ViewChild link. Another part of the story is that my actual source has objects like someArray which I'd like to be reset in each render. I'm not sure re-rendering resets someArray though. Currently, I'm resetting them in the ngOnInit method.

So, my question is: how do I trigger re-rendering for changes on deeper elements of a parent object?

Thanks

Numen answered 6/7, 2017 at 6:48 Comment(3)
Possible duplicate of Triggering Angular2 change detection manuallyTanker
@Tanker , I don't want to trigger a change detection process, I want Angular to recognize a change, which cannot be detected with its builtin change recognition mechanism...Numen
@suat, it is detected, see my answer. I added some clarificationJepson
J
32

As far as I understood that unless the complete item object is changed, angular2 does not recognize the changes on item.

It's not all that straightforward. You have to distinguish between triggering ngOnChanges when object is mutated and DOM update of the child component. Angular doesn't recognize that item is changed and doesn't trigger a ngOnChanges lifecycle hook, but the DOM will still be updated if you reference particular property of the item in the template. It's because the reference to the object is preserved. Therefore to have this behavior:

And afterwards, make the child component i.e. CompB re-rendered as if angular detected a change in the regular way.

You don't have to do anything in particular because you will still have update in the DOM.

Manual change detection

You can insert a change detector and trigger it like this:

@Component({
    selector: 'compA',
    template:  template: `<compB [item]=item></compB>`
})
export class CompA {
    item:any;
    constructor(cd: ChangeDetectorRef) {}

    updateItem():void {
        item[name] = "updated name";
        this.cd.detectChanges();
    }
}

This triggers change detection for the current component and all its children.

But, it won't have any effect in your case because even though Angular doesn't detect change in item it still runs change detection for the child B component and updates the DOM.

Unless you use ChangeDetectionStrategy.OnPush. In this case a way to go for you would be to do a manual check in the ngDoCheck hook of the CompB:

import { ChangeDetectorRef } from '@angular/core';

export class CompB implements OnInit{
    @Input() item: any;
    someArray: any[];
    previous;

    constructor(cd: ChangeDetectorRef) {}

    ngOnInit():void {
        this.previous = this.item.name;
        someArray.push("something");
    }

    ngDoCheck() {
      if (this.previous !== this.item.name) {
        this.cd.detectChanges();
      }
    }
}

You can find more information in the following articles:

Jepson answered 6/7, 2017 at 6:52 Comment(2)
Hi i have tried your sample code but ngDoCheck() method executing several time in the application can you explain how to overcome this issueRoof
Not sure, but I suppose you additionally need changeDetection: ChangeDetectionStrategy.OnPush or detach from change detection?Selfassured
T
3

you can put another input in CompB so when you want change properties of item in CompA just change value of this input.

@Component({
    selector: 'compA',
    template:  template: `<compB [item]=item [trigger]=trigger></compB>`
})
export class CompA {
    item:any;
    trigger: any;
    updateItem():void {
        item[name] = "updated name";
        trigger = new Object();
    }
}

@Component({
    selector: 'compB',
    template:  template: `<p>{{item[name]}}</p>`
})
export class CompB implements OnInit{
    @Input() item: any;
    @Input() trigger: any;
}
Tapia answered 6/8, 2019 at 5:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.