Roman Mahotskyi
Roman Mahotskyi

Reputation: 6685

How to detect when Angular has attached elements to the DOM?

There is a component:

<my-component></my-component>

it's component receives a stream of numbers as @Input

<my-component [data]="numbers$ | async"></my-component>

inside the component we update the model:

@Component({...})
class MyComponent {

  numbers: number[] = [];

  @Input
  set data(numbers: number[]) {
      this.numbers = numbers;
  }

  constructor() {}

  myCallback() { }
}

then render the list of numbers, like this:

<ul>
 <li *ngFor="number of numbers">{{number}}</li>
</ul>

How to detect that *ngFor has rendered all elements? And how to invoke callback function myCallback after elements have been appended to the DOM?

Upvotes: 5

Views: 7835

Answers (3)

Muhammed Albarmavi
Muhammed Albarmavi

Reputation: 24464

You can create a directive to detect the elemntmrender to the dom and run a method at that time

import { Directive , Output ,EventEmitter } from '@angular/core';

@Directive({
  selector: '[rendered]'
})
export class RenderedDirective {

   @Output() rendered:EventEmitter<any> = new EventEmitter();

   ngAfterViewInit() {
     this.rendered.emit()
   }

}

ngAfterViewInit will called after Angular has fully initialized a and render li elemnt

MyComponent

export class MyComponentComponent {

  numbers: number[] = [];

  @Input() set data(numbers: number[]) {
    this.numbers = numbers;
  }

  myCallback(elm) {
    console.log(elm)
  }

}

template

<ul>
    <li #li *ngFor="let n of numbers" (rendered)="myCallback(li)">
        {{n}}
    </li>
</ul>

stackblitz demo 🔥🔥

Upvotes: 2

Muhammed Albarmavi
Muhammed Albarmavi

Reputation: 24464

You can use ViewChildren to get the QueryList of elements or directives from the view DOM. Any time a child element is added, removed, or moved, the query list will be updated, and the changes observable of the query list will emit a new value.

import { Component, OnInit, Input, ViewChildren, QueryList } from '@angular/core';

@Component({
  selector: 'my-component',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.css']
})
export class MyComponentComponent {

  numbers: number[] = [];
  @ViewChildren('li') elements: QueryList<any>;

  @Input() set data(numbers: number[]) {
    this.numbers = numbers;
  }

  ngAfterViewInit() {
    this.elements.changes.subscribe(li => {
      li.forEach(elm => this.myCallback(elm.nativeElement))
    })
  }

  myCallback(elm) {
    console.log(elm)
  }

}

template

<ul>
    <li #li *ngFor="let n of numbers">
        {{n}}
    </li>
</ul>

myCallback will log each elemnt but you still has access to all li elemnts

stackblitz demo 🚀🚀

ViewChildren

Upvotes: 7

Alexander_F
Alexander_F

Reputation: 2877

this happens in NgInit

https://angular.io/guide/lifecycle-hooks

ngOnInit()
Initialize the directive/component after Angular first displays the data-bound properties and sets the directive/component's input properties.

Called once, after the first ngOnChanges().

Update:

I have propably misunderstood your question. You dont have any update on the ngFor. What is your usecase? Maybe there are different solutions for this.

Upvotes: 1

Related Questions