Angular2 change detection "Expression has changed after it was checked"
Asked Answered
P

1

0

I have an issue in Angular2 (final) around change detection and data flow between components. I have worked around it but it seems a little hacky to me so was wondering if there is a better way of doing it.

Basically I have a component A that has a child component B and also *ngFor that creates number of children components C.

For every component C, there is an @Output defined that is handled in the parent component A for each instance of C. Based on that output, another property on component A is determined (just a number) and thats used as @Input on component B.

In DEV mode, every time the @Output in components C is triggered, I am getting below error in the console:

Expression has changed after it was checked. Previous value: XX. Current value: XX.

From reading about it, its kind of expected due to uni-directional data flow in Angular2. I wonder how to make it work properly in my scenario?

I temporarily injected ChangeDetectorRef in component A and call its detectChanges() method in the function that handles @Output event from instance of C. I am worried that its not very efficient tho. I could probably try improve it and call it only after the last item's event (all the C components send that event at the same time) but then I would be worried about async nature of the events and some unexpected behaviors.

Does anyone have an idea how to overcome this problem? Is my design fundamentally flawed in terms of the data flow between the components? Should I go for something like a shared service instead to exchange data?

Any help much appreciated.

Portend answered 23/1, 2017 at 13:24 Comment(2)
The error is thrown when a change detection run causes a model change. For example if you change the model in ngOnChanges() which is called by change detection. We would need to see more code to track the cause down. Often it's caused when a function is being bound to that returns a new object instance every time it is called.Danger
@Günter Zöchbauer that is kind of what happens. Every instance of component C has ngOnChanges() that emits the event and results in model change in the parent component A. So is that something that should be avoided?Portend
W
4

You can inject private cdRef:ChangeDetectorRef and call this.cdRef.detectChanges() at the end of ngOnChanges(). This way Angular runs change detection again and won't complain about previously modified model values.

class MyComponent {
  constructor(private cdRef:ChangeDetectorRef) {}

  ngOnChanges(changes) {
    this.xxx = ...
    ...
    this.cdRef.detectChanges();
  }
}
Weeper answered 23/1, 2017 at 13:34 Comment(2)
In my case it wont work if I do this in ngOnChanges, as that happens in children of component A, and in turn changes the value in another child B of component A (by emitting the event back to A and A updating relevant property thats used as input for child B). However it will work in the component's A method that handles the events received from C. I just thought that its a "hacky" to do it in Angular2 and was wondering should I rethink the way I pass data around between components.Portend
Usually using shared services with observables is a good idea. Observables don't cause isdues with change detection.Danger

© 2022 - 2024 — McMap. All rights reserved.