I'm encountering a simple problem which has a hacky solution of setTimeout(...,0).
Looking at this simple code :
@Component({
selector: 'my-app',
template: `
<div>
<input value='Fill Data' type='button' (click)='fill()'/>
<span *ngFor="let o of Items" class='mySpan'>Span To Detect<br></span>
</div>
`,
})
export class App {
Items:Array<number> = new Array<number>();
fill()
{
this.Items = [1,2,3,4,5,6,7,8,9,10]
this.analyzeDom(); //this has to run here
}
analyzeDom()
{
alert($("div .mySpan").length) // "0"
//BUT if I set this hacky trick , it works
// setTimeout(function (){ alert($("div .mySpan").length)},0) // "10"
}
}
If I click the button , the alert shows "0". I understand why it's happening. It's becuase Angular didn't complete its cycle to actually populate the ngFor.
However - doing this trick with setTimeout(..,0) seems a bit hacky to me and I prefer not to trust on it.
Question:
What is the right way to "wait for operation" in Angular ? (so that I'll see "10") ?
Plnkr
1) You can force Angular to update the DOM by calling cdRef.detectChanges
constructor(private cdRef: ChangeDetectorRef) {}
analyzeDom(){
this.cdRef.detectChanges();
alert($("div .mySpan").length)
Plunker
As setTimeout is macrotask therefore it is running next digest cycle.
It means that after calling setTimeout all components tree will be checked
cdRef.detectChanges doesn't call appRef.tick(). It only executes change detection component itself and its children.
2) or you can wait until Angulat has updated DOM by subscribing to zone.onMicrotaskEmpty
import 'rxjs/add/operator/first';
constructor(private zone: NgZone) {}
...
this.zone.onMicrotaskEmpty.first().subscribe(() => this.analyzeDom());
Note: first operator can cause memory leak. See https://github.com/angular/material2/issues/6905
Subscribing to onMicrotaskEmpty doesn't call change detection cycle
Plunker
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