【问题标题】:Retrieve elements created by ngFor in ngAfterViewInit在 ngAfterViewInit 中检索由 ngFor 创建的元素
【发布时间】:2018-07-27 00:40:29
【问题描述】:

我正在我的 Angular (5) 应用程序中实现一个惰性图像加载器,我很好奇如何避免在我的 ngAfterViewInit() 中调用 setTimeout(),如果可能的话。

代码的相关部分是:

# component
ngOnInit(): void {
  this.workService.getCategories().then(workCategories => {
    this.workCategories = workCategories;
  });
}

ngAfterViewInit(): void {
  setTimeout(() => {
    const images = Array.from(document.querySelectorAll('.lazy-image'));
  }, 100);
}

# component template
<div *ngFor="let workCategory of workCategories">
  <h3>{{ workCategory.fields.name }}</h3>
  <div *ngFor="let workSample of workCategory.fields.workSamples">
    <img width="294" height="294" class="lazy-image" src="..." data-src="..." />
  </div>
</div>

如果我删除 setTimeout(),图像数组总是空的。 AfterViewInit 应该在所有子组件创建后运行。我也试过 AfterContentInit,它的行为和 AfterContentChecked 一样,它使 Chrome 崩溃。

这种情况下是否可以避免setTimeout?

【问题讨论】:

  • 您是否有任何具体原因试图直接访问 DOM 节点?通常,在使用 angular 时,您希望尽可能少地直接访问 dom。例如,您是否尝试设置 onload 事件以在延迟加载中使用?
  • 我正在使用 IntersectionObserver 的实现将原始 src 替换为包含实际图像 URL 的数据属性。
  • 我们可以看看你在ngOnInit 中做了什么以及带有lazy-image 元素的标记部分吗?
  • 我添加了 OnInit 和部分模板标记。简而言之,我检索工作类别,然后迭代每个类别的样本。
  • 刚试了changeDetectorRef策略,图片数组还是空的。我可以坚持使用 setTimeout,我只是好奇是否有更好的方法。

标签: javascript angular


【解决方案1】:

This stackblitz 展示了一种在使用ngFor 指令创建元素时获得通知的方法。在模板中,您将模板引用变量#lazyImage 分配给img 元素:

<div *ngFor="let workCategory of workCategories">
  ...
  <div *ngFor="let workSample of workCategory.fields.workSamples">
    <img #lazyImage width="294" height="294" class="lazy-image" src="..." data-src="..." />
  </div>
</div>

在代码中,@ViewChildren("lazyImage") 用于声明与这些图像关联的QueryList&lt;ElementRef&gt;。通过在ngAfterViewInit 中订阅Querylistchanges 事件,您会在元素可用时收到通知。然后可以从QueryList 检索 HTML 元素:

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

@Component({
  ...
})
export class AppComponent {

  @ViewChildren("lazyImage") lazyImages: QueryList<ElementRef>;

  ngAfterViewInit() {
    this.lazyImages.changes.subscribe(() => {
      let images = this.lazyImages.toArray().map(x => x.nativeElement);
    });
  }
}

如果只处理最后创建的项目,可以使用QueryList.last

    this.lazyImages.changes.subscribe(() => {
      this.doSomethingOnLastImage(this.lazyImages.last);
    });

【讨论】:

  • 谢谢。我会在早上试试这个。
  • 这非常有效。我需要再看一下 ViewChildren 的文档。非常感谢您的帮助!
  • 谢谢!在这种情况下,这是在“等待”最后一个项目被渲染吗?
  • @DA - 每次ngFor 循环的内容发生变化时都会触发QueryList.changes 事件,并且在那个时刻执行事件处理程序。如果同时添加多个元素,则事件仅触发一次。 the stackblitz 中的 console.log 声明表明了这一点。
  • 啊哈!那讲得通。谢谢!
【解决方案2】:

您可以使用requesAnimationFrame API。

您的问题是,即使 Angular 告诉浏览器渲染图像,它们还没有渲染,它需要一些时间来完成并更新 DOM,这就是您的数组为空的原因。

requestAnimationFrame API 要求浏览器(通过回调方法)告诉您它何时完成当前任务,何时完成渲染。

ngAfterViewInit(): void {
  window.requestAnimationFrame(() => {
    //place your code here
  })
}

【讨论】:

  • 嗯。这对我来说仍然是一个空数组。
  • 你是在这个组件中获取图片的url还是通过@input传递它们?
  • 它们是从组件中的 ngOnInit() 中的服务调用中检索到的。
  • 那么你应该在加载数据后调用requestAnimationFrame api,而不是在ngAfterViewInit()
  • 对不起,如果我在 observable 返回值后直接调用该方法,它仍然返回一个空数组。
猜你喜欢
  • 2021-02-04
  • 1970-01-01
  • 2018-09-09
  • 1970-01-01
  • 1970-01-01
  • 2020-08-09
  • 1970-01-01
  • 2019-07-13
  • 2018-08-17
相关资源
最近更新 更多