New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
EXCEPTION: Expression has changed after it was checked. #6005
Comments
I have been facing a similar issue, and appears to be due to how a child is initialized, and how a parent might update the child as part of its initialization process. I have a similar sample with an ugly workaround using zone: http://plnkr.co/edit/GI45u805pnFUtFeORnxn?p=preview, with the workaround looking like the following: constructor(private _zone : NgZone) { } ngAfterViewInit() : void { |
This is not a bug, it's a feature of dev mode working as intended. Calling That said, it's being thrown for good reason: In short, after every round of change detection, dev mode immediately performs a second round to verify that no bindings have changed since the end of the first, as this would indicate that changes are being caused by change detection itself. In your plunk, you have the binding |
I need to setup my component in |
You just need to do something, anything, that triggers another round of change detection during that method - emit an event, whatever. Wrapping it in a timeout (queue flashback to ng1 :-P) is one way that'd work, but feels super messy to me in ng2. |
It doesn't look user friendly. I think of that method as of the place where I can complete my component initialization when its children are defined and now it turns out that it has some custom rules. I think you should internally force new change detection round so that it is hidden inside ng2 from the user. |
could you post a more representative sample of what you're trying to achieve here? if its your image spinner, you'd be better using a standard binding. |
@AerisG222 ditto for you. is there any reason you can't use a simple binding to achieve this? overriding zone behavior is typically not going to be recommended. |
@robwormald Yea, in that case I could do the property binding. The real case I have also involves another property which is a bit more complicated - an array of custom objects, but that too certainly could be set via property binding. I suppose if there was no such thing as Another minor point is when looking through an application using the property binding syntax, you have to inspect the template to see who is ultimately consuming the data. This would be more obvious when wired via code. |
I have cleaned the code to show only what matters for the example. The idea is to show spinner only while image is loading and then remove spinner and show image. Image is loaded only when element is visible on screen.
import {Component, Input, Renderer, ElementRef, AfterViewInit, ViewChild} from 'angular2/core';
@Component({
selector: '[inflate]',
templateUrl: './inflate.html',
styleUrls: ['./inflate.css']
})
export class Inflate implements AfterViewInit {
@Input('inflate') url: string;
@ViewChild('holder') holder: ElementRef;
isLoaded = false;
isLoading = false;
constructor(private renderer: Renderer) {}
ngAfterViewInit() {
setTimeout(_=> this.inflate()); // BUGFIX: https://github.com/angular/angular/issues/6005#issuecomment-165911194
}
inflate() {
let bounds = <ClientRect>this.holder.nativeElement.getBoundingClientRect();
if(bounds.bottom > 0 && bounds.top < window.innerHeight) {
this.isLoading = true;
let img = new Image();
img.src = this.url;
img.onload = _=> {
this.isLoaded = true;
this.isLoading = false;
this.renderer.setElementStyle(this.holder,
'background-image', 'url("' + this.url + '")');
};
}
}
}
<image-holder #holder></image-holder>
<spinner *ngIf="isLoading"></spinner> |
I need to keep image not in host because while it fades-in with animation on load complete it shouldn't affect host background opacity. |
I've got a potentially related problem with a nested directive. |
@svi3c you are actually returning a different object. Each function call will return a new object instance that is "equal" to the previous one in terms of keys / values but not in terms of instances / references. There are number of ways of dealing with this situation:
You might want to check https://github.com/ng-bootstrap/core/blob/master/src/progressbar/progressbar.ts as another example of a progressbar directive for ng2 (using bootstrap's HTML / CSS) |
@pkozlowski-opensource Thank you very much! |
Can you advise what is the correct and simplest way to re-trigger change detection within ngAfterViewInit - assuming all that is needed is a property to be updated. Based on the comments here it seems to be a fairly common requirement to update a bound property in ngAfterViewInit (due to a dependency on a child component), so Angular 2 seems to be missing a simple way to do this. If updating a bound property in ngAfterViewInit is the wrong thing to be doing, what is the alternative? |
Here's a quick one: Challenge: Expectation: Reality: ngAfterViewInit() {
window.setTimeout(() =>
this.elementHeight = (this.nativeElement.offsetWidth * this.randomImage.dim.h) / this.randomImage.dim.w
)
} |
@Birowsky Plunker? |
@zoechi Here is an example: http://plnkr.co/edit/MML5uHEFQdL0k0TA5HtY You can toggle the |
I have also run into something similar when trying to implement the |
I have another example. It's quite a bit simpler in that a |
Same as my issue #5950. |
Some recommendations here on how to do this cleanly. However, I'm still wrapping in |
I am using the HTML5 media source extensions to create an audio context based on a video element, so use of |
@ElsewhereGames Hard to tell without seeing some actual code. Can you create a Plunker |
@zoechi I don't have an actual bug to report, just stating that not accessing the DOM directly, while desirable, it not always something that can be avoided (in response to @robwormald comment). For the actualy API, see the docs on createMediaElementSource. |
I posted back in January with an ngModel problem. Now, I'm back again with ViewChildren problem. However, unlike some of these other examples, I'm not actually trying to do anything in my demo. Meaning, my controllers have zero logic - it's all meta data and view bindings. As such, I am not sure where I even have wiggle room to mess things up: |
@DiegoGarciaa ... yes, this is a typical example where async and sync operation are mixed and meet together in one CD and its confirmation in dev mode. I will not repeat the explanation from the post https://hackernoon.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4, so maybe try to read it first. And look also to https://hackernoon.com/here-is-how-to-get-viewcontainerref-before-viewchild-query-is-evaluated-f649e51315fb. |
So the article basically says that I must manually call a round of change detection... Let's be honest I'm very upset with current status... even the minimal things in angular are so boring and tiring... I can't even breath. FUck you |
@DiegoGarciaa ... no, it is just an auxiliary solution. You can also use Or just create an instance of the dynamic component in the moment when you will have the data. So your subscription will invoke the creation of the dynamic component instance. But what is important -> the primary logic why the error happens -> because unidirectional changes should flow always from top to bottom through the whole tree and after one CD cycle there is a confirmation of the status that unidirectional principle was respected. |
This "fuck you" was not personal... just venting my frustations. We use a directive schema to handle our components. We built a helper that loads a component into a component view reference, and as we understand, the data must be avaliable when a component is being loaded protected loadPartialView(viewName: string, target: ViewContainerRef) : ComponentRef<any> {
const factories = Array.from(this.resolver['_factories'].keys());
const factoryClass = <Type<any>>factories.find((x: any) => x.name === viewName);
const factory = this.resolver.resolveComponentFactory(factoryClass);
return target.createComponent(factory);
} In the example above: I would like to do something like this: return target.createComponent(factory, dataSource); Where my dataSource would be avaliable at constructor time Like redux, that uses a object to handle datasource passing through components, we are thinking about to implement an injectable component that handles data for us, so we can get it before the lifecicle hooks. |
diegogarciaa I don't think that data must be available before ... a dynamic component is still a component with
I suppose that you need to use also components from lazy loaded modules so I use a map directly in a module to have a possibility to access components using just string names.
What And maybe the new @angular/cdk module would be interesting for you. Just intro here: https://medium.com/@caroso1222/a-first-look-into-the-angular-cdk-67e68807ed9b. |
For what it worth and if it can help anybody, instead of using constructor(
private zone: NgZone,
) { }
ngAfterViewInit() {
this.zone.onStable.first().subscribe(() => {
// your code here
});
} I am not fully aware of the consequences of doing this but can it be worst than using |
@AerisG222 solution work for me
|
this is the html component :
|
This is certainly a complex issue, but for some cases (especially when you are modifying properties on a ContentChild) it can be solved by moving the logic to ngAfterContentInit() instead of ngAfterViewInit() or ngOnInit() |
It's amazing how simple things can cause so much confusion in angular |
@jakeNiemiec lame job, troll, no one is searching Angularjs anymore |
Yes, I would expect that more people would need to google "angular tutorial" out of desperation, hence that initial comment on "confusion". But don't worry, we can check npm for exact usage stats: http://www.npmtrends.com/react-vs-@angular/cli-vs-vue-vs-@angular/core Just compare the amount of issues. I would really like to see angular get better, but I suspect that Vue.js will pass it in the distant future. Having programmed in all 3, I highly recommend Vue to angular devs. It's like angular without the boilerplate. |
ok answer accepted, I am perfectly fine with those stats. |
@jakeNiemiec ... 50% of issues being created is mistakenly placed there ... they are support / knowledge issues in fact and should not be there at all. The fun is that the most of the required topics is documented very well in the official docs + the answers are based on the knowledge of JavaScript. But you know, people don't read the docs deep enough. |
@HostBinding can be a confusing source of this error. Consider a simple binding on a component, such as That property is checked by the **parent **component's change detection as if it'owned' it. From our perspective we want think of it as the child component 'owning' that property. So seems like there should be a way for a component to exclude properties for checking in this case? Or is it more complicated than that? I had to have my parent component run More complete description: https://stackoverflow.com/questions/43375532/expressionchangedafterithasbeencheckederror-explained/51662105#51662105 |
In angular 4, that work 4 me <3 |
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
What's wrong here? http://plnkr.co/edit/nm8OkrpZCIp4cvA6TbpO?p=preview
The text was updated successfully, but these errors were encountered: