Reputation: 1406
Here is the stackblitz code, where I am emitting event from child component when the API call is made using the service.ts
file.
My child.comp.ts
import { Component, Output,EventEmitter } from '@angular/core';
import {Service} from './service';
@Component({
selector: 'child',
template: `Child data: {{data}}`
})
export class ChildComponent {
@Output() change = new EventEmitter();
data: string;
constructor(private svc: Service){}
ngOnInit(){
this.change.emit(true);
this.svc.getMsg()
.subscribe( data =>{
this.data = data;
this.change.emit(false);
})
}
}
My app.comp.ts
import { Component ,ChangeDetectorRef} from '@angular/core';
import {Service} from './service';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular 6';
show= false;
constructor(svc: Service,private crd: ChangeDetectorRef){}
showData(event){
this.show = event;
// this.crd.detectChanges(); // this stops the error
}
}
As you can see that adding this.crd.detectChanges();
stops the error by letting angular
know that this is part which requires another change detection.
But, I came across this answer on SO where this error basically represents BAD design. and using detectChanges()
seems more like a fix as I have to write it explicitly.
Can someone help me with the GOOD design where I don't have to write it explicitly detectChanges()
at the places where the values will change in lifecycles.
Upvotes: 3
Views: 1454
Reputation: 17504
So, I investigated what should be the best approach to implement what you have written. I asked the same question to Max. What I can conclude is that
Angular follows Unidirectional flow of data, as you can read over here
In your case, you are basically changing the value of show
in the middle of CD. What happens now, is that when CD is triggered 2nd time in DEV mode, it finds out that you have changed parent data from child component using EventEmitter
. This contradicts with the Unidirectional flow of Angular. Possible solutions in your case,
Use this.crd.detectChanges()
to let the angular know explicitly that you have changed the value , and so the angular will be kind enough to run CD again and won't throw any error
OR, implement the show
behavior in the child itself rather than going against the flow of Parent -> Child.
Upvotes: 7
Reputation: 12376
When you run your app in dev mode (default), the change detector walks through the tree of your components twice:
So ask yourself if you really need to emit this message from ngOnInit that can cause UI changes elsewhere? If you can't find a better place to do it, try to move the code in the setTimeout() function, which will ensure that this code is invoked on the next event loop cycle, when change detector is done with its pass.
Upvotes: 1