I'm having a problem with an Angular 6 application:
Let's say I have 2 components: parent and child.
the child has 2 inputs. While 1 input changes, inside the ngOnChanges() the child component emit somthing to the parent component.
Then the parent component changes the second input for the child component. I expect the change detection of the child component to get called once again- BUT IT'S NOT. The view of the child component shows old value of the second input.
the application has the following defenitions:
To illustrate the case, I've created a simple demo project. There I don't use store, so I don't have any selectors.. instead I'm using rxjs Subject for the use of async pipe.
Here it is:
https://angular-ccejq4.stackblitz.io
Take a look the the console to understand what happens and when.
What i've already tried:
I found that the new value arrives to the async pipe. the async pipe then calls markForCheck(). but it doesn't call detectChanges().. so a change detection cycle won't run. the view will change properly only at the next cycle of change detection- as you can see on my demo app.
Any ideas?
thanks!
When that happens, the book is added directly to the cached list of books, or updated in place. As for the components, you have a parent component which contains a child component that lists the books and another child component that allows for the editing of those books. Those two child components are the ones with the OnPush change detection set.
So now how to update the value in the child component without changing the onPush strategy, the one rule here is to always use an immutable way of passing input objects like instead of modifying object directly pass the new reference of the object. Let's modify our code accordingly in the parent component.
The ngOnChanges () is a built-in Angular callback method invoked immediately after the default change detector checks data-bound properties if at least one has changed. Before the view and content, children are checked. interface OnChanges { ngOnChanges ( changes: SimpleChanges): void } Let’s understand this by an example.
The Default strategy runs every time any change happens in the app. It could be a button click, an HTTP call, a setTimeout, or any other type of timer or user interaction. The OnPush strategy on the other hand, only runs if one of four conditions are met:
Let me first say, great question with detail and well explained. You have indeed hit a caveat of the angular change detection.
From within ngOnChanges
there is no direct way to trigger another change detection. Otherwise you will have the possibility to hit a loop. Also performance wise they do not allow this. You need to find a way to get into the next execution cycle of JS
The setTimeout
is not a bad option to overcome this. You can either put this around the emit:
setTimeout(() => this.secondEmitter.emit(this.first));
or you can put it around a markForCheck
call:
setTimeout(() => this.cdRef.markForCheck());
But I agree with you that it feels a bit hacky, but sometimes you just need to cope with it. If you really cannot, there is another way. Your BehaviorSubject
is by nature a synchronous observable. You can however make the async
subscription in your template to use the asyncScheduler
:
private second = new BehaviorSubject(0);
readonly second$ = this.second.asObservable().pipe(
observeOn(asyncScheduler)
)
constructor() {
this.second.next(0);
this.second$.subscribe((lastval) => {
console.log(`second subscription update to ${lastval}`);
});
}
From the last option I've made a fork of your stack
This will make any subscription to it receive the message after the next event loop cycle.
Update
Coming back to this, a better solution would be to use the async
option on the creation of the EventEmitter
.
In that case you can use .emit
on the second observable:
@Output() firstEmitter:EventEmitter<number> = new EventEmitter();
// notice the 'true'
@Output() secondEmitter:EventEmitter<number> = new EventEmitter(true);
ngOnChanges(changes:any) {
if (changes.first) {
this.secondEmitter.emit(this.first);
}
}
working example
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With