问题
When trying to access to a DOM of the document, I find that the template is only partially executed. If I use a setTimeout
I can workaround the problem, but how is the proper way to do it?
import { Component, Input, AfterContentInit } from '@angular/core';
import { AppService } from './app.service';
@Component({
selector: 'tabs',
templateUrl: './templates/app.html',
providers: [ AppService ]
})
export class ShowBookmarksComponent implements AfterContentInit {
private addListeners():void {
let timeoutId = setTimeout(() => {
let draggables = document.getElementsByClassName('session');
console.log(draggables);
console.log(draggables.length);
for (let i = 0; i < draggables.length; i++) {
console.log(i+' '+draggables.length)
}
clearTimeout(timeoutId);
}, 1000);
}
ngAfterContentInit() {
this.appService.getBookmarkLists(this.sessions)
.then(() => this.appService.getCurrentTabs(this.current_tabs))
.then(() => this.addListeners());
}
}
appService
is providing data that the template will use to display the html. The template:
<section class="current_tabs">
<div class="session" *ngFor="let current_tab of get_keys(current_tabs);">
<div class="whatever"></div>
</div>
</section>
<section class="current_bookmarks">
<div class="session" *ngFor="let session of get_keys(sessions);">
<div class="whatever"></div>
</div>
</section>
And somehow*, the part corresponding to sessions
is filled but the part of current_tabs
is not yet filled. Both this.sessions
and this.current_tabs
are correctly filled before calling addListeners
*seen as well by console.log(document.documentElement.innerHTML)
executed before the setTimeout
<section class="current_tabs">
<!--template bindings={
"ng-reflect-ng-for-of": ""
}-->
</section>
<section class="current_bookmarks">
<!--template bindings={
"ng-reflect-ng-for-of": "my_session_201 etc etc etc (correctly filled)
(when executing it inside the setTimeout, both are correctly filled)
In the code, what I want is to get the let draggables = document.getElementsByClassName('session');
correctly retrieved, but I am not getting the ones defined within <section class="current_tabs">
since it is not yet filled
EDIT: more core Iterate over TypeScript Dictionary in Angular 2
get_keys(obj : any) : Object {
return Object.keys(obj);
}
回答1:
You need to wait until the current_tabs
are set, then invoke change detection explicitly (detectChanges()
), then you can add the listeners directly afterwards:
constructor(private cdRef:ChangeDetectorRef){}
ngOnInit() {
this.appService.getBookmarkLists(this.sessions)
.then(() => this.appService.getCurrentTabs(this.current_tabs))
.then(() => {
this.cdRef.detectChanges();
this.addListeners();
});
}
回答2:
AfterViewChecked is called very frequently by angular to update state of the page when new data is available (bindings, ajax requests etc). You should try to put your code in AfterContentInit which is called when all external data is loaded to page.
see https://angular.io/docs/ts/latest/api/core/index/AfterContentInit-class.html
回答3:
Solved in Angular2, HostListener, how can I target an element? can I target based on class? by @GünterZöchbauer. The problem is that between the 2 functions
.then(() => this.appService.getCurrentTabs(this.current_tabs))
.then(() => this.addListeners());
the renderer needs to display the view. So I should call addListeners
when the view is updated. Done with
.then(() => {
this.cdr.detectChanges();
this.addListeners();
});
来源:https://stackoverflow.com/questions/41695838/angular2-templates-are-only-partially-executed-when-using-ngaftercontentinit-or