Vorpal56
Vorpal56

Reputation: 71

Sibling component does not receive emitted changes

In my Angular 9 project I have 2 components which are siblings and the parent component. On change in component A, I emit a value and it's set in the parent component and calls a method in component B. The method in component B emits another value and it's set in the parent component. The on change in component A continues, but the emitted value from component B that is set in the parent component (which is an input in component A) is not changed. I don't know why it's not the input for component A does not change even though the parent updates the value.

Parent Component

setSomeNum(someNum: number) {
    // this is run on someNumberEmitter in Component A
    this.num = someNum;
    if (this.viewChildComponentB) {
        this.viewChildComponentB.remove(someNum);
    }
}
setSomeOtherNum (someOtherNum: number) {
    // this is run on someDiffNumEmitter in Component B
    this.otherNum = someOtherNum
}

Component A

componentAOnChange(someNum: number) {
    this.someNumberEmitter.emit(someNum);
    // this.inputFromComponentB is the original value instead of the one emitted in Component B (this.someDiffNum)
    this.someService.applyCalc(someNum, this.inputFromComponentB);
}

Component B

remove(someNum: number) {
    this.someDiffNumEmitter.emit(this.someDiffNum);
    this.someService.applyCalc(someNum, this.someDiffNum);
}

I'm using the OnPush change detection strategy, but nothing changed. How can the sibling component A run with the data changes from component B?

Upvotes: 0

Views: 321

Answers (1)

Yair Cohen
Yair Cohen

Reputation: 2268

I'm not sure why you're using ViewChild there but if it is to update the child components manually when there's change then that should be a red flag something is being done wrong, if you have data that needs to be shared it should be shared across the board and update accordingly on the single source of data changes without having to manually update the rest of the places.

Now to your problem:

If you're using the OnPush change detection strategy you have to update your data in an immutable way or use Observables, otherwise the change detection won't trigger.

Some people will advice triggering change detection manually but I'd recommend avoiding that as the whole point of using OnPush is to avoid a whole page render unnecessarily.

A simple solution I like to use is to use a Subject or BehaviorSubject instead with the async pipe. This way it ensures smooth work with the OnPush change detection strategy because ChangeDetection will run when the Observable emits a new value, and the async pipe takes care of unsubscribing the Observable for you.

If I were to modify your current components, it'd look something like this:

Parent:

num$ = new Subject<number>();
otherNum$ = new Subject<number>();

setSomeNum(someNum: number) {
    this.num$.next(someNum);
}

setSomeOtherNum (someOtherNum: number) {
    // this is run on someDiffNumEmitter in Component B
    this.otherNum$.next(someOtherNum)
}

Then in the HTML you can use the async pipe, like this:

<some-component [num]="num$ | async" [otherNum]="otherNum$ | async"></some-component>

(You could use the async pipe in the component itself, doesn't really matter).

And that's pretty much it. You should have a Subject as an Observable, then share it with child components, once the Observable is updated, the child components data will be updated as well.

One small caveat is that when using a Subject instead of a BehaviorSubject is to make sure to subscribe before emitting any values to the Subject, otherwise the data will not update. So for certain cases BehaviorSubject is a better fit.

Upvotes: 1

Related Questions