admin管理员组文章数量:1424881
I'm implementing a lazy image loader in my Angular (5) app, and am curious how I can avoid having to call setTimeout()
in my ngAfterViewInit()
, if possible.
The relevant portions of the code are:
# ponent
ngOnInit(): void {
this.workService.getCategories().then(workCategories => {
this.workCategories = workCategories;
});
}
ngAfterViewInit(): void {
setTimeout(() => {
const images = Array.from(document.querySelectorAll('.lazy-image'));
}, 100);
}
# ponent 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>
If I remove setTimeout()
the images array is always empty. AfterViewInit should run after all of the child ponents have been created. I've also tried AfterContentInit, which behaves the same and AfterContentChecked, which crashed Chrome.
Is it possible to avoid setTimeout in this case?
I'm implementing a lazy image loader in my Angular (5) app, and am curious how I can avoid having to call setTimeout()
in my ngAfterViewInit()
, if possible.
The relevant portions of the code are:
# ponent
ngOnInit(): void {
this.workService.getCategories().then(workCategories => {
this.workCategories = workCategories;
});
}
ngAfterViewInit(): void {
setTimeout(() => {
const images = Array.from(document.querySelectorAll('.lazy-image'));
}, 100);
}
# ponent 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>
If I remove setTimeout()
the images array is always empty. AfterViewInit should run after all of the child ponents have been created. I've also tried AfterContentInit, which behaves the same and AfterContentChecked, which crashed Chrome.
Is it possible to avoid setTimeout in this case?
Share Improve this question edited Mar 12, 2018 at 2:01 Martin Parenteau 73.9k13 gold badges135 silver badges155 bronze badges asked Mar 11, 2018 at 23:30 Brandon TaylorBrandon Taylor 34.6k16 gold badges109 silver badges153 bronze badges 5- Is there any specific reason you are trying to grab direct access to the DOM nodes? Usually you want to do the least amount of direct access to dom as possible when using angular. For instance are you trying to setup the onload events for use in the lazy load? – Patrick Evans Commented Mar 11, 2018 at 23:49
- I'm using an implementation of the IntersectionObserver to replace the original src with a data attribute containing the actual image url as they e into view. – Brandon Taylor Commented Mar 11, 2018 at 23:54
-
Can we see what you do in
ngOnInit
and the part of the markup with thelazy-image
element? – Martin Parenteau Commented Mar 12, 2018 at 0:37 - I've added the OnInit and a portion of the template markup. In a nutshell, I retrieve the work categories, then iterate over the samples for each category. – Brandon Taylor Commented Mar 12, 2018 at 0:46
- Just tried the changeDetectorRef strategy, and the array of images is still empty. I'm ok sticking with setTimeout, I was just curious if there was a better approach. – Brandon Taylor Commented Mar 12, 2018 at 1:01
2 Answers
Reset to default 5This stackblitz shows one method to get notified when the elements have been created with the ngFor
directive. In the template, you assign a template reference variable #lazyImage
to the img
element:
<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>
In the code, @ViewChildren("lazyImage")
is used to declare a QueryList<ElementRef>
associated to these images. By subscribing to the changes
event of the Querylist
in ngAfterViewInit
, you get notified when the elements are available. The HTML elements can then be retrieved from the QueryList
:
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);
});
}
}
In cases where only the last created item is to be processed, the QueryList.last
can be used:
this.lazyImages.changes.subscribe(() => {
this.doSomethingOnLastImage(this.lazyImages.last);
});
You can use the requesAnimationFrame
API.
Your problem is that even though Angular told the browser to render the images, they are not rendered yet, it takes some times for it to do so and update the DOM, that's why your array is empty.
The requestAnimationFrame API asks for the browser to tell you (via a callback method) when it's done with it's current tasks, when rendering is plete.
ngAfterViewInit(): void {
window.requestAnimationFrame(() => {
//place your code here
})
}
本文标签: javascriptRetrieve elements created by ngFor in ngAfterViewInitStack Overflow
版权声明:本文标题:javascript - Retrieve elements created by ngFor in ngAfterViewInit - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745422306a2657955.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论