#angular
#angular
Вопрос:
Существует компонент:
<my-component></my-component>
этот компонент получает поток чисел в виде @Input
<my-component [data]="numbers$ | async"></my-component>
внутри компонента мы обновляем модель:
@Component({...})
class MyComponent {
numbers: number[] = [];
@Input
set data(numbers: number[]) {
this.numbers = numbers;
}
constructor() {}
myCallback() { }
}
затем отобразите список чисел следующим образом:
<ul>
<li *ngFor="number of numbers">{{number}}</li>
</ul>
Как определить, что *ngFor
отрисовало все элементы? И как вызвать функцию обратного вызова myCallback
после добавления элементов в DOM?
Ответ №1:
Вы можете использовать ViewChildren
для получения списка запросов элементов или директив из view DOM. Каждый раз, когда дочерний элемент добавляется, удаляется или перемещается, список запросов будет обновляться, и изменения, наблюдаемые в списке запросов, будут выдавать новое значение.
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)
}
}
шаблон
<ul>
<li #li *ngFor="let n of numbers">
{{n}}
</li>
</ul>
MyCallback будет регистрировать каждый элемент, но у вас по-прежнему будет доступ ко всем элементам li
Комментарии:
1. Спасибо за ваш ответ. Я хотел бы подчеркнуть, что мне нужно определить момент, когда angular отрисовал элементы в DOM (после отправки данных). В вашем примере мы просто вызываем
myCallback
после ввода чисел, на этом шаге<li>
элементы еще не отображаются. Чтобы быть более конкретным, я хочу использоватьdocument.querySelector('')
для получения доступа к элементам списка.2. @RomanMahotskyi Я обновил ответ с помощью
ViewChildren
декоратора 👍
Ответ №2:
Вы можете создать директиву для обнаружения elemntmrender в dom и запустить метод в это время
import { Directive , Output ,EventEmitter } from '@angular/core';
@Directive({
selector: '[rendered]'
})
export class RenderedDirective {
@Output() rendered:EventEmitter<any> = new EventEmitter();
ngAfterViewInit() {
this.rendered.emit()
}
}
ngAfterViewInit будет вызван после того, как Angular полностью инициализирует a и отобразит li elemnt
MyComponent
export class MyComponentComponent {
numbers: number[] = [];
@Input() set data(numbers: number[]) {
this.numbers = numbers;
}
myCallback(elm) {
console.log(elm)
}
}
шаблон
<ul>
<li #li *ngFor="let n of numbers" (rendered)="myCallback(li)">
{{n}}
</li>
</ul>
Ответ №3:
это происходит в ngInit
https://angular.io/guide/lifecycle-hooks
ngOnInit()
Инициализируйте директиву / компонент после того, как Angular впервые отобразит свойства, связанные с данными, и установит входные свойства директивы / компонента.Вызывается один раз, после первого ngOnChanges().
Обновить:
Я, вероятно, неправильно понял ваш вопрос. У вас нет никаких обновлений для ngFor. Каков ваш вариант использования? Возможно, для этого есть разные решения.
Комментарии:
1. корректные данные, которые могут быть асинхронными, могут стать после рендеринга компонента, проверьте консоль в этом примере stackblitz.com/edit/angular-f9v5dj
2. @malbarmawi хорошее решение, но есть обратный вызов для данных, а не для рендеринга. Или это не так?
3.
ngOnInit
вызывается только один раз после первогоngOnChanges
. Мне нужно вызватьmyCallback
функцию для каждогоnumbers$.next([1,2,3])
, но сразу после того, как эти элементы будут переданы в DOM.4. @Fribu-SmartSolutions исправляют, первое решение не сработает, вот почему я использую
ViewChildren
декоратор