Search code examples
angulartypescriptlifecycledomcontentloaded

Angular2, templates are only partially executed when using ngAfterContentInit or ngAfterViewInit functions


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);
}

Solution

  • 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();
        });
    }