What's wrong here? http://plnkr.co/edit/nm8OkrpZCIp4cvA6TbpO?p=preview
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._zone.overrideOnTurnDone(() => {
this.child.title = 'title from parent';
});
}
This is not a bug, it's a feature of dev mode working as intended. Calling enableProdMode( )
- see updated plunk when bootstrapping the app prevents the exception from being thrown.
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 [attr.spinner]=isLoading
, and isLoading
changes as a result of your call to this.load()
, which occurs when ngAfterViewInit
is fired, which happens as a part of the initial change detection turn. That in itself isn't problematic though - the problem is that this.load()
changes a binding but does not trigger a new round of change detection, which means that this binding will not be updated until some future round of change detection.
I need to setup my component in afterViewInit
because I have ChildView
that is initialized here. What is the correct way to make that change to isLoading
? Wrap call to this.load
by setTimeout
?
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 @ViewChild
, I wouldn't care too much, as this would be the only reasonable way to pass the information down. However, I was very excited to see @ViewChild
so that I could work off of that to component references in code. This simplifies the markup and allows more control via code, which is additionally valuable with typescript as there is more tooling support (like intellisense and compile time checking). It also simplifies the container component as it does not have to track member variables which are intended only to be used as state for the child.
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.
inflate.ts
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 + '")');
};
}
}
}
inflate.html
<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.
http://plnkr.co/edit/myX2Alx9jie2q0FDIME7?p=preview
Here I'm binding to ngStyle in the progressBar.ts.
I'm logging the styles object before returning to prove that they are equal.
I also get the ExpressionChangedAfterItHasBeenCheckedException .
@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:
ChangeDetectionStrategy.OnPush
strategy so your function is only invoked when one of input changes, ex: http://plnkr.co/edit/NWyVoTtkiAzqWJphLMbi?p=previewYou 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!
I only read https://angular.io/docs/ts/latest/guide/template-syntax.html#!#ngstyle and I did not stumble upon change detection strategies yet. That's great! :)
@drew-moore:
You just need to do something, anything, that triggers another round of change detection during that method - emit an event, whatever.
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. ApplicationRef.tick()
doesn't work here as that causes 'ApplicationRef.tick is called recursively' exception; emitting an event just to trigger a change detection feels wrong (and didn't work for me), and so does setTimeout
.
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:
Set the dom element's height (via a certain formula) based on it's rendered width.
Expectation:
When element get's rendered, get the width and based on it, calculate it's height.
Reality:
setTimeout?!
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 setTimeout
in app/app.component.ts#21
in the ngAfterViewInit
lifecycle hook.
I have also run into something similar when trying to implement the writeValue( newValue )
method of a custom ngModel
valueAccessor
. When I try to apply the newValue to the component, it tells me the value has changed (in a bad way). Like you all have found, wrapping it in a setTimeout() "fixes" it; but, not in a good way.
I have another example. It's quite a bit simpler in that a @HostBinding()
is set up and is informed by a QueryList
.
Same as my issue #5950.
Some recommendations here on how to do this cleanly.
http://stackoverflow.com/questions/34827334/triggering-angular2-change-detection-manually
However, I'm still wrapping in setTimeout
because I couldn't get any of them to do what I need. It looks like I could ChangeDetectorRef.detectChanges()
at the end of a callback and have trigger the CD...
I am using the HTML5 media source extensions to create an audio context based on a video element, so use of @ViewChild
is required. Based on the audio context I am trying to display some info, like number of channels. However, I ran into this issue as well. What is the right way to bypass this?
@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_:
Running into similar issue but could rewrite the code to work around it. I had a good laugh with @bennadel post so thank you. Share some of the feeling with previous issues I had where the smart change detection was either not smart enough, too smart, or too smart for me:-)
https://github.com/angular/angular/issues/6618
So far always had a way to workaround it, but will suggest it here again at the very least the change detection mechanism could use more documentation. @bennadel Your issue reminds me of another issue,
https://github.com/angular/angular/issues/5177
where to resolve this it was suggested that extra life cycle hook be added OnBeforeBinding/OnBeforeRendering. It seems like that solution would also solve your issue.
@tandu just replace ngAfterViewInit
to ngOnInit
@e-oz https://github.com/angular/angular/issues/6005#issuecomment-165906207
@tandu it doesn't really matter when your spinner will start (loading child, loading image in child - for user it all is loading), only matters when you need to turn it off. You can turn it off in "ngAfterViewInit", using setTimeout (I agree it's ugly), but it will be wrong - child component should fire an event when image loading completed, to the parent component. Loading child component and loading image are 2 different events.
@e-oz https://github.com/angular/angular/issues/6005#issuecomment-165951692 I access to child bounds to detect if I should start image loading.
@tandu and you could move all this check to the child component code (it would be legit in ngAfterViewInit of the child component) and sent event to parent, when to change isLoading.
@e-oz I define component as #holder
in html: <image-holder #holder></image-holder>
. So there is no js file for it.
so create it, otherwise there is no need to use ngAfterViewInit, if View
doesn't have real components as children - nothing will be rendered.
On Sun, 13 Mar 2016 at 15:18 tandu [email protected] wrote:
@e-oz https://github.com/e-oz I define component as #holder in html:
. So there is no js file for it. holder>
—
Reply to this email directly or view it on GitHub
https://github.com/angular/angular/issues/6005#issuecomment-195963389.
I can as well use element.getElementsByTagsName
or similar methods and not use ChildComponent. There are definitely exist workarounds. I'm fine with setTimeout
fix. The problem is that method name suggests that it can be used to define some logic that wants child components to be initialized and bounded to component but the method doesn't tell that it is run in the middle of change detection cycle and so we have long thread of complaints from different people.
@tandu I absolutely agree with you in this, it's very annoying limitation.
For what it is worth, I've changed my original workaround to the following, which is starting to feel like a reasonable approach. Perhaps this is worth a try for your situation:
constructor(private _changeDetectionRef : ChangeDetectorRef) { }
ngAfterViewInit() : void {
// your code
this._changeDetectionRef.detectChanges();
}
I'm getting the same exception when using form validation (required input). Related to this issue?
Plunker: http://plnkr.co/edit/Fp6XT5mC6ZCB14Z1gvTi?p=preview
@eivindr had a quick look and I had not seen ngFormControl on an input before, can't see why or what it would mean as it should go on the form element to bind it to a control group and the individual input would have a corresponding ngControl. In any case in your plunker remove
[ngFormControl]="formModel.find('title')"
and it fixes your error.
I refuse to drop this issue :) Yesterday, I was able to demonstrate that my issue was specifically related to host bindings that depend on input properties:
I have to believe that this is a bug at this point because the component has two things that depend on the Input - the view template and the host bindings - and _only one of them_ is causing the change exception. If both of them caused the issue, I would think something else; but, since only one of them is causing the issue, it must be a bug, right?
@eivindr I also have the same issue when starting out with a blank form then setting the value of the form controls programatically. Did you find a work around?
@MarkPerryBV No, I'm still getting "has changed after it was checked" when using required. Same when using pattern validator. But minLength etc works.
@user414 If I remove [ngFormControl]... there is no "required" validation on the input.
@MarkPerryBV I've found a workaround that works for me (for now). I had to enableProdMode(). I'm using ngIf on the inputs, so I also had to do a ChangeDetectorRef.detectChanges() after the input was rendered (if not, the input was invalid despite having content).
Using enableProdMode()
doesn't really fix anything - it just masks the symptom by stopping it from doing the double-check and informing you something is probably wrong.
AerisG222's fix seems better than using a setTimeout
. Is it really the case that the intended behavior is to have to manually trigger change detection after calling addControl()
with a validator?
Same problem here after calling addControl(). I think addControl() should call ChangeDetectionRef.detectChanges().
@CaptainCodeman I know - it was a temporary workaround.
This will no longer fail in dev mode when I'm using ng2@beta17, [email protected], zone.[email protected]. I still have to call ChangeDetectionRef.detectChanges()
@eivindr can you update your plnkr to the beta17 fix?
I'm loosing my mind over this one, I refuse to submit to enableProdMode()
.
@AerisG222 Your approach works for me!
Is using the approach that @AerisG222 posted a good approach to use? Yes it does work but are there any consciences of manually triggeringchangeDetectionRef.detectChanges()
?
I'm having the same issue when attempting to do dynamic form validation and then resetting the form. here is my plunk
Steps to reproduce. Submit form with Is Employee Checked. After form refreshes check Is Employee again.
I am using the changeDetectionRef.detectChanges() in the submit function.
thanks for this @AerisG222 :)
I don't like all that setTimeout workaround. I propose the following:
http://plnkr.co/edit/9umnTGsdFWhbaOBeftuZ?p=preview
Using ChangeDetectorRef.detectChanges
is not always a good approach. Example why:
Parent component calls child's components public API during its ngAfterViewInit
. This call changes child's binding. Calling detectChanges
from parent would solve the issue too, but that means parent must know when to call detectChanges
after which child's API call. You will notice that calling detectChanges
in childs method which changes the binding in this case does not work when you want to use ChangeDetectionStrategy.OnPush
.
I encountered this error when dynamically adding children via ViewContainerRef.createComponent()
and then updating the child instance. I'm using RC2 (and trying not to use depreciated stuff).
Initially thing worked fine (first render), but then I added a component that would emit new data. This event was listened to by the parent component, which updated it's data array triggering change detection and the population of new dynamic child components. The event was triggered by a form submit. If I pressed the submit button, everything was fine. If I hit enter to generate the submit it would produce the error.
I ended up adding ChangeDetectorRef.detectChanges()
immediately following the creation and update of the dynamic component. Not a huge issue, but it seems odd that even though I've just added a new component to the view the system doesn't automatically trigger change detection for the child.
@aerisg222 Do i need to call this._changeDetectionRef.detectChanges() After every children property change from parent?
Well I just encountered this issue. I have two sibling components using the the same data object. Both objects are suppose to be able to update the data source. The other source actually see the change in the object but triggets the Expression has changed after it was checked. Previous value: 'true'. Current value: 'false'
This happened to me after upgrading from RC1 to RC4
Same here. Started getting this again after upgrading from RC1 to RC4.
Maybe the change that caused this should have been communicated as a breaking change in the change log...kuz my stuff is broken now.
We need to wait for final.
I stopped using child components because updating external properties sucks.
I thought zones was here for that...
Same here. Dynamic component loading via ViewComponentRef seems to behave differently in RC2 and RC4 as it did in RC1
Here are my working dependencies:
"dependencies": {
"@angular/common": "2.0.0-rc.1",
"@angular/compiler": "2.0.0-rc.1",
"@angular/core": "2.0.0-rc.1",
"@angular/http": "2.0.0-rc.1",
"@angular/platform-browser": "2.0.0-rc.1",
"@angular/platform-browser-dynamic": "2.0.0-rc.1",
"systemjs": "0.19.31",
"core-js": "^2.4.0",
"reflect-metadata": "^0.1.3",
"rxjs": "5.0.0-beta.6",
"zone.js": "^0.6.12"
},
"devDependencies": {
"typescript": "^1.8.10",
"typings":"^1.0.4",
"concurrently": "^2.2.0",
"lite-server": "^2.2.2"
}
And here are the dependencies that are producing the mentioned error:
"dependencies": {
"@angular/common": "2.0.0-rc.4",
"@angular/compiler": "2.0.0-rc.4",
"@angular/core": "2.0.0-rc.4",
"@angular/http": "2.0.0-rc.4",
"@angular/platform-browser": "2.0.0-rc.4",
"@angular/platform-browser-dynamic": "2.0.0-rc.4",
"systemjs": "0.19.31",
"core-js": "^2.4.0",
"reflect-metadata": "^0.1.3",
"rxjs": "5.0.0-beta.6",
"zone.js": "^0.6.12"
},
"devDependencies": {
"typescript": "^1.8.10",
"typings":"^1.0.4",
"concurrently": "^2.2.0",
"lite-server": "^2.2.2"
}
RC2 isn't working either!
Just a question but is there any way to get more information out on the error? For example it would be nice to know what the "expression" is when the error says "expression changes from 'true' to 'false'!.
I'm also having exact same issue after upgrading from RC1 to RC4. And what I figured out it is due to ngOnInit
function in my Component. If I comment out this function, the error message went away and everything works fine. Any thoughts?
Closing this issue as it is old and actions have been taken. If you still have problem, make sure to create a new issue and fill out the issue template with all the required details. Thanks
but people were saying they are still experiencing this issue in new rc...
On Wed, 13 Jul 2016 at 00:52, Victor Berchet [email protected]
wrote:
Closed #6005 https://github.com/angular/angular/issues/6005.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/angular/angular/issues/6005#event-720794445, or mute
the thread
https://github.com/notifications/unsubscribe/AAgIEFEVZmE0M53XxNpzAYg9hJT_E_viks5qVAyGgaJpZM4G4J_s
.
Yes, I just upgraded to RC4 and didn't make a single change in any of my component other than configuring a new router and ran into this issue. It was all good in RC1, seems like this is still around.
More Info:
It turns out due to async
call inside ngOnInit
, this error occurs. If I comment out the async call code or move that piece of code to the constructor, everything works fine.
@ammar91
Call this after changes :
changeDetectionRef.detectChanges()
@zigzag95 Yes this is a workaround indeed but it feels super hacky to do this. I would expect a framework like angular to handle this more gracefully rather than making us manually trigger its own change detection mechanism.
In my case, it is happening when I'm subscribing to angular event emitter in app.component.ts
to show and hide the progressbar. It was all good with RC1 but break in RC4.
Here is the repro, http://plnkr.co/edit/jEtfUrQdc4sJBySj5kWN?p=preview
Click on Heroes Link and select one of the hero to edit and you will see the error.
Please see the changes in app.component.ts
and hero.service.ts
Hey @vicb, thanks for the update. Can you tell us what actions have been taken? Do you mean like this will be fixed in the next release?
Another simple alternative solution that worked for me is to return the value as observable and delay emitting values by 10ms. In my case the exception was occurring with spinner value
<app-spinner *ngIf="spinner"></app-spinner>
this.eventService.toggleSpinnerAnnounced$.subscribe(
(show: boolean) => this.spinner = show
);
this worked for me :
private spinner = new Subject<boolean>();
toggleSpinnerAnnounced$ = this.spinner.asObservable().delay(10); // This delay() is important
announceSpinnerToggle(show: boolean) {
this.spinner.next(show);
}
This worked for you because you are using the delay and it's not related to observable.
You will have the same behaviour when using stettimeout.
@Jarvens please stop posting this comment on unappropriate places.
GitHub issues are for bug reports and feature requests.
For support questions please use other channels like the ones listed in CONTRIBUTING - Got a Question or Problem?
Your comment doesn't provide enough information to diagnose your problem.
FYI: I got this error because of the chrome extension ng-inspector for AngularJS
(angular 1 extension alternative to batarang)
This is nuts! After reading the whole thread what fixed the issue in development mode was disabling ng-inspector for AngularJS as @KarlGustav-unimicro says.
With that said ChangeDetectorRef.detectChanges()
also worked for me but felt really hacky.
@pkozlowski-opensource,
i still have no idea why indicatorStyle()
will be invoked when one of input changes,
can u give more explanation ?
If i implement ngDoCheck
in _progress-bar component_ without setting ChangeDetectionStrategy.OnPush
in parent compoent,
i found out that ngDoCheck
execution two times,
i know why the first one will be executed,
but i really confused about the second ngDoCheck
...
@LiTiang,
i still have no idea why indicatorStyle() will be invoked when one of input changes,
can u give more explanation ?
Since indicatorStyle()
is bound to [ngStyle]
in the view, it will always be invoked when the change detector runs. This will happen regardless of whether the inputs have actually changed or not.
i found out that ngDoCheck execution two times,
i know why the first one will be executed,
but i really confused about the second ngDoCheck ...
This is most likely because, as mentioned earlier in this thread, the change detection always runs twice in dev mode. It does this to verify that we don't have code that changes bindings during change detection, which might cause difficult bugs in production.
I wanted the height of a dynamic <ul> block to control the height of an <img> I have bound to "companionHeight".
So evil... But the following works using an Observable.
<ul class="title-list-ul" #listul (window:resize)="setCompanionHeight(listul)">
<img class="title-list-companion-img" [src]="getCompanionImageURL()" [height]="companionHeight"/>
import { Component, OnInit, AfterViewChecked, ElementRef } from '@angular/core'; // redacted
import { Observable } from 'rxjs';
@Component({
selector: 'resume-title-list',
templateUrl: './title-list.component.html',
styleUrls: ['./title-list.component.scss']
})
export class TitleListComponent implements OnInit, AfterViewChecked {
private error: Object;
private companionHeight: number = 0;
private companionHeightLast: number = 0;
// Lots of irrelevant stuff like ngInit redacted for simplicity.
constructor(private elementRef: ElementRef) { } // redacted, mine is more complicated.
setCompanionHeight(listElement: any) {
let clientRect = listElement.getBoundingClientRect();
if (clientRect) {
this.companionHeight = clientRect["height"];
}
}
// window::resize only happens when the window is resized: This is an initialization
// function that sets the initial size of the image... Without it the image height is
// initially zero and the image is not shown.
// The height was not correct in ngAfterViewInit, so I used ngAfterViewChecked instead.
ngAfterViewChecked() {
let ularray = this.elementRef.nativeElement.getElementsByClassName("title-list-ul");
if (ularray && ularray.length > 0) {
let h = ularray[0].getBoundingClientRect().height;
if (h && h != this.companionHeightLast) {
this.companionHeightLast = h;
Observable
.of(h)
.delay(10)
.subscribe(h => this.companionHeight = h,
error => this.error = error);
}
}
}
}
Comments on closed issues might not be read by Angular team members.
What about posting a question in one of these channels CONTRIBUTING - Got a Question or Problem? with a Plunker to reproduce?
I had the same problem with:
<div *ngIf="isLoading">
<app-some-viewchild></app-some-viewchild>
</div>
Usin [hidden] instead *ngIf solve the problem.
I met this issue too. Finally, I found that the default inition object is same with the ngModle object.
setting the changeDetection: ChangeDetectionStrategy.OnPush
in the @Component
decorator fixed it for me
@elvismercado thanks, solves my issue in 2.1.1!
I had a child component relying on a value passed by the parent template, with the child template throwing this exception when the parent value changed. Adding your suggestion to the child component causes change detection to not run until the changed value is pushed to the child component, avoiding detection to run prematurely I assume?
If I have an input that has focus and then remove it from the DOM (ngIf condition), the error "Expression has changed after it was checked" occurs (only if the input has a form group associated).
Here is a plunker with the issue:
https://plnkr.co/edit/dooRvC1gY1WEcaNdshoP?p=preview
Thanks!
So what's the solution here?
Changing any input value in ngOnInit will trigger this.
@coli
just wrap into
setTimeout(()=>{
///your code here
}, 1);
I have a combination of child components that needs to be preloaded when a user wants to edit an existing entity.
I have been struggling with this issue for hours until I managed to fix it using @drewlio post above.
Inject _changeDetectionRef:
constructor(
private _changeDetectionRef: ChangeDetectorRef) {
}
And then right after making your change call detectchanges.
This did the trick for me.
setSizeFromCtrl() {
console.log("setSizeFromCtrl");
if (this.sizeFromList && this.tradeProfile && this.tradeProfile.SizeFrom && this.sizeFromCtrl) {
this.sizeFromCtrl.select(String(this.tradeProfile.SizeFrom));
this._changeDetectionRef.detectChanges();
console.log("setSizeFromCtrl DONE");
}
}
setCentreTypeCtrl() {
console.log("setCentreTypeCtrl");
if (this.centreTypeList && this.tradeProfile && this.tradeProfile.centreTypeList && this.centreTypeCtrl) {
for (let i of this.tradeProfile.centreTypeList) {
this.centreTypeCtrl.select(i.value);
}
this._changeDetectionRef.detectChanges();
console.log("centreTypeCtrl DONE");
}
}
Note that I had this same problem but a different (similar?) cause and different solutions.
ngOnInit()
. Some of these forms are collapsed with an ngIf
in the templatengOnInit()
and was being applied with a theModelForm.patchValue(data)
ngOnInit()
that didn't fire until it was expanded. Take out the patchValue()
and the problem went awaypatchValue()
run a this._changeDetectionRef.detectChanges();
. And change the ngIf
to a [hidden]
so that the form and DOM is fully built. The down-side of this is the time and memory cost as the complete form and the related DOM is created (this may not be a problem in many apps but it was for us as we have a gigantic form).patchValue()
(or setValue()
if the data and fields are 1:1) in the components and thus only happens when the form is created in ngOnInit()
.I haven't committed these changes yet (22/3/17).
So I do not think that this decision will save everyone.
But at me occurrence of the given error was accompanied by creation of a new element of a class. Then adding it to the array, which is output via *ngFor.
I solved this problem by adding a constructor to my class, which populated the fields required for displaying in the template the default values (zeros, empty lines)
export class Task {
task_id: number;
....
constructor() {
this.task_name = '';
this.task_desc = '';
this.task_percent = 0;
addNewTask():void{
let newTask = new Task();
this.tasksList.push(newTask);
}
task_name & task_desc & task_percent I'm displaying in the template. If at least one of the listed fields is not initialized in the constructor, I get a similar error.
+1
AerisG222 solution work
constructor(private _changeDetectionRef : ChangeDetectorRef) { }
ngAfterViewInit() : void {
// your code
this._changeDetectionRef.detectChanges();
}
this._changeDetectionRef.detectChanges() works, but it will cause other issues in my case. I just wrap the code inside the setTimeout() function, it solves it.
@GuofuHuang doing that is a way of avoiding the problem not fixing it which can sometimes lead to infinite Change Detection cycles
@Toxicable Sorry, probably I didn't make it clear, I'm not using this._changeDetectionRef.detectChanges(); Instead, I wrap my source code inside the setTimeout().
@GuofuHuang if whatever you put inside the setTimeout
causes CD and you're running that on a lifecycle hook then it's possible you're causing infinite CD cycles. The solution is not make changes which cause CD during a CD run
@Toxicable Even I set it 0?
@GuofuHuang if setting to 0 causes a CD then yes
@Toxicable I just find this link, http://stackoverflow.com/questions/779379/why-is-settimeoutfn-0-sometimes-useful, it says that setTimeout of 0 is something like pause/yield in C, I don't know if it will cause any CD, but now it works perfect in mine. If not this solution, do you have better one to solve this problem?
@GuofuHuang I don't know what pause/yield does in C but in JS it'll place the callback to the end of the event queue. My solution is as above: The solution is not make changes which cause CD during a CD run
settimeout is the solution
You don't need to specify the ms parameter and I will wait one tick
@zigzag95 @GuofuHuang see this example for why setTimeout
is not the solution
https://plnkr.co/edit/dv8K9EvVQLG59Gxsl3oI?p=preview
@Toxicable Thanks, but my case is very different from this one, and I don't see any infinite loop. But this is definitely a good example helps me understand angular more.
@GuofuHuang Indeed it will not happen in your case because you're doing it in ngAfterViewInit
which is only called once. My point was that it's possible to cause an infinite loop with this method, hence it's dangerous to use. That is what the plunkr showing.
@moutono Your tricks have helped me! Great thanks 😄
As the problem is about triggering a change detection, resolving a promise might also work because ngZone monkey patches promises as well. This one has worked for me:
ngAfterViewInit() {
Promise.resolve().then(() => your_code_here);
}
Yeah setTimeout works, but i didn't like that fix, in my case it was caused because i was changing value on a EventEmitter
The solution was change
private generalSubscriber: EventEmitter<any> = new EventEmitter<any>();
to
private generalSubscriber: EventEmitter<any> = new EventEmitter<any>(true);
weird right?
Well that true means, the eventemitter is asyncronous
https://angular.io/docs/ts/latest/api/core/index/EventEmitter-class.html
is there any solution to this? Why is it closed?
I have to clear cache every time
in my case I was replacing an image stub on image load, had to put it in a setTimeout
@intergleam @wasa4587 @caroso1222 @tandu @AerisG222 @drew-moore @robwormald @svi3c @pkozlowski-opensource @pcroc @zoechi @stefankoru @bennadel @bryanforbes @Necroskillz @drewlio @ElsewhereGames @zoechi @user414 @e-oz
It error facing while developing. On production server working fine.
@smartHasmukh it's a development-mode only error, therefore no surprise.
You should still fix it.
If this error happens then most likely you break one direction data flow. Which could be something like changing data of one component in the sibling, changing data of parent in the children's lifecycle hooks maybe some more thing.
This exception thrown is not an error, but the helper for developer to understand that one direction data flow contract is broken in some way. The fact that this works in production is by accident, you should review how you are changing your data in parent-children communication, and fix it in development mode.
@smartHasmukh I used to find this uncomfortable also. For example, I hook into navigation events and further emit an event to the main component to show a loader. And in each component I then hide that loader, after I receive the data from the server (so, within a subscription). That's fine. But, in one component, I only got some data from a "static" source, no observable there, so I simply tried to use ngOnInit to tell the main component to hide the loader. Then I got the error, of course.
Even though my case should be a simple thing, it's not the right way to do it. I had to either take the decision to not show the loader in the first place, for that component (which could get more tricky if I'd have more components like that)... Or, also handle hiding the loader from my data service, instead of at component level. Or, an ugly fix, would be to wrap that one specific case in a setTimeout(). Because the initial approach wasn't the right one.
TL/DR:
I'm sure there has to be a proper way for your scenario as well, other than a _setTimeout()_ ugly fix (which I've _sadly_ chosen in that moment, but then again, it's a very simple scenario and I know what's going on there - yet, I might have to change it at some point, in a future release).
You could try StackOverflow for your specific case, give more details. There has to be a way.
Sure Thanks @MrCroft @tytskyi and I will do it Mr. @zoechi and I know It is no surprise. but I just letting know. nothing else
@tytskyi
The fact that this works in production is by accident, you should review
No, in most of the cases my response to this error is "yes, I know, and it's intended". A lot of components in my apps are loading something after init and after receiving new values in bound variables. This issue is just "known ugliness" of Angular and nobody cares.
@e-oz if you are affecting somehow parent or sibling state in any of the lifecycle hooks then this is breaking of one direction data flow. You should make such changes when application state is stabilized, but not in the middle of change detection. That's why there are those tricks with microtask, setTimeout 0 etc.
@tytskyi calm down with your "should", I'm making changes after stabilization - it's obvious if read carefully - http request to load additional data will, obviously, take more time than initial stabilization. You will not convince me that this behaviour of Angular is correct, don't even try.
@e-oz how is that related to a HTTP request. An HTTP request response causes change detection and you won't get the "Expression has changed ..." error.
Can you please provide more information?
@zoechi there is no reason to waste time on it, nobody will change nothing, task is closed.
@e-oz the issue was closed a year ago and the Angular team had quite different priorities back then (get a release out) and this issue wasn't a blocker back then.
If you think you have a valid case you should create a new issue.
@zoechi I think not you will decide what I should to do. I can create a new issue, but I don't have to. Due all respect, I don't like that "you should" tone.
@e-oz I see, your comments are only to vent your frustration and you're not interesting in getting a solution. Please stop wasting other peoples time here with your useless comments.
if you are calling http, subscribe into your component, just add..
Promise.resolve().then(() => your_code_here);
or
myObservable.delay(10).subscribe(...)
One of the two solutions will solve your problem
@vicb Stop closing issues that are not fixed. Why are you doing this ?
At least suggest some properly fix, but closing this is unacceptable
@diegogarciaa ... and what exactly is not fixed or is wrong from your point of view? The conversation ^^^ is so long that is hard to find the actual core.
@mlc-mlapis with all due respect, I am not your teacher, if you can't read or understand the problem, stop bothering. I spent long minutes reading all the content, I recommend the same.
The angular team(not all team, but who are here closing this issue) attitude seems so enterprisy: Not blocking(my salary), not fixing it anytime soon.
@diegogarciaa ... very good. Continue like above and you'll be welcome everywhere.
@mlc-mlapis I don't want to be welcome where people can't read and try to bullshit where should not.
@diegogarciaa ... I just thought that you personally have a problem and it looks that it is not true now. And the reality is also that the most of the problems with Expression has changed after it was checked ...
are not bugs but just not right understanding how CD works.
@mlc-mlapis ok... let's understand
http://plnkr.co/edit/nm8OkrpZCIp4cvA6TbpO?p=preview
In this plunk I can understand the variable was bound and checked when ngAfterViewInit was called, therefore when it receives a new value, and do not trigger a new check round, the exceptions is thrown
But for example, If I do have a view container ref... and I use for example:
this.service.GetData().subscribe(
response => this.component.instance.dataModel = response
);
Considere that dataModel is being used as {{ dataModel.Field }} in my html
this.component is a dynamic component being loaded at certain events, how one could avoid the error? Which would be the correct way to pass the data to my component before the lifecicle hooks of change detection
@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 promise.then
or use setTimeout
... but the recommended solution in this case is probably a shared service so you will not update directly the property of dynamic component cross the CD cycle and its confirmation.
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 @Input()
and @Output()
but the question is when you will change the data.
... the data must be avaliable when a component is being loaded ...
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.
export default class LazyLoadedModule {
cmps: Map<{}, {}> = new Map();
constructor() {
this.cmps.set('first-component', FirstComponent);
...
}
}
What return target.createComponent(factory, dataSource);
should mean? Because actual API is createComponent(componentFactory, index, injector, projectableNodes, ngModule)
.
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 setTimeout
and as using ChangeDetectorRef.detectChanges()
didn't work for me, I ended up using NgZone.onStable
and execute my code by subscribing once on the EventEmitter
...
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 setTimeout
? If anybody has any comment on that matter it would be really welcome!
@AerisG222 solution work for me
constructor(private _changeDetectionRef : ChangeDetectorRef) { }
ngAfterViewChecked() : void {
// your code
this._changeDetectionRef.detectChanges();
}
this is the html component :
{{ lastData.note[i].date | date : 'dd.MM.yyyy'}} | {{ lastData.note[i].date | date: 'HH:mm' }} : #BFL15_200817 | {{ lastData.status.name }} by {{lastData.note[i].admin}} I got the error if the data in the array is more than one, Any solution?
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
Don't worry, everyone else has noticed as well 😕
@jakeNiemiec lame job, troll, no one is searching Angularjs anymore
https://trends.google.com/trends/explore?date=all&q=Angular%20tutorial,React%20tutorial
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.
I imagine there are more people using PHP in India that Java in the whole world. One would be an idiot to use PHP for a large scale enterprise app, so you should use your own brain sometimes, life will be better
@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 @HostBinding('style.background')
.
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 detectChanges
(or add an extra div in the child) to avoid the error in the case where the child component was simply changing its own background color (and never doing it anywhere else).
More complete description: https://stackoverflow.com/questions/43375532/expressionchangedafterithasbeencheckederror-explained/51662105#51662105
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.
In angular 4, that work 4 me <3
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
_This action has been performed automatically by a bot._
Most helpful comment
This is not a bug, it's a feature of dev mode working as intended. Calling
enableProdMode( )
- see updated plunk when bootstrapping the app prevents the exception from being thrown.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
[attr.spinner]=isLoading
, andisLoading
changes as a result of your call tothis.load()
, which occurs whenngAfterViewInit
is fired, which happens as a part of the initial change detection turn. That in itself isn't problematic though - the problem is thatthis.load()
changes a binding but does not trigger a new round of change detection, which means that this binding will not be updated until some future round of change detection.