Angular ViewChildren 不会立即看到来自 ngFor 的所有孩子

Posted

技术标签:

【中文标题】Angular ViewChildren 不会立即看到来自 ngFor 的所有孩子【英文标题】:Angular ViewChildren does not see all children from ngFor immediately 【发布时间】:2019-01-14 16:41:58 【问题描述】:

我有一个奇怪的@ViewChildren 行为,对应于ngFor 生成的子组件。 @ViewChildren 查询在很长一段时间内都看不到数组中的元素。我所有的代码都在Plunker 中——打开控制台查看。

这是我的主要组件:

@Component(
    selector: 'my-app',
    template: `
        <button (click)="addInternalComponent()">Add internal component</button>
        <app-internal #internals *ngFor="let i of indexes" [index]="i
(afterViewInit)="onAfterViewInit()"></app-internal>
    `,
)
export class App 
    @ViewChildren('internals') internals: QueryList<InternalComponent>;
    indexes = [];
    addInternalComponent() 
        console.log('adding internal component!');
        this.indexes.push(this.indexes.length);
        console.log('Not complete list', this.internals._results);

    

    onAfterViewInit() 
        console.log('onAfterViewInit - still not complete list', this.internals._results);
    

我们可以通过单击按钮添加哪些子组件。

在将一个元素添加到索引数组中后,该元素会在 ngFor 循环中生成所有子元素 - 我们没有刚刚添加的元素。

我可以理解这种行为 - 因为可能子组件需要一些时间来创建它,并且引擎决定在创建完整的子组件之前控制台日志。

但是,我在子组件中创建了发射器,表明子视图和模型都已初始化。但是......在父组件中处理这个事件 - 我们仍然不知何故没有这个项目。这怎么可能?

子组件:

export class InternalComponent implements AfterViewInit 
  @Input()
  index;
  @Output()
  afterViewInit: EventEmitter<any> = new EventEmitter<any>();

  ngAfterViewInit() 
    this.afterViewInit.emit();
  


【问题讨论】:

【参考方案1】:

通知QueryList 的内容已更改的标准方法是在ngAfterViewInit 中订阅其changes 事件:

@ViewChildren("internals") internals: QueryList<InternalComponent>;

ngAfterViewInit() 
  this.internals.changes.subscribe((list: QueryList<InternalComponent>) => 
    // The updated QueryList is available here (with list or with this.internals)
    this.doSomethingWithInternals(list);
    this.doSomethingWithNewInternal(list.last);
    ...
  );


上面的事件处理可能就是你所需要的。如果还想在InternalComponent中实现afterViewInit事件,可以将组件的引用作为事件的参数传递:

export class InternalComponent implements AfterViewInit 
  @Output() afterViewInit = new EventEmitter<InternalComponent>();

  ngAfterViewInit() 
    this.afterViewInit.emit(this);
  


并在事件处理程序中检索组件:

(afterViewInit)="onAfterViewInit($event)"

onAfterViewInit(component: InternalComponent) 
    this.useNewInternalComponent(component);
    ...

【讨论】:

以上是关于Angular ViewChildren 不会立即看到来自 ngFor 的所有孩子的主要内容,如果未能解决你的问题,请参考以下文章

Angular DOM 操作:为啥 ViewChildren 有效而 ViewChild 无效

使用 ViewChildren 访问子组件的指令

Angular 子组件不会立即更新

角度自动目录

viewchildren 的 offsettop 和 offsetleft

dart:用查询替换 ViewChildren