I have a simple template:
<div *ngFor="let skill of allSkills | async ">
<div class="skill-chart-with-img">
<skill-chart-view></skill-chart-view>
<div #skillImg>
some data
</div>
</div>
</div>
When I try to bind on #skillImg, I have an empty QueryList:
@ViewChildren("skillImg") private skillImgs: QueryList<ElementRef>;
ngAfterViewInit(): void {
this.skillImgs.forEach((skill: ElementRef) => {
//some work with skill
});
}
Maybe I miss something or my task have a better solution?
Another critical difference is that @ViewChild returns a single native DOM element as a reference, while the @ViewChildren decorator returns the list of different native DOM elements in the form of QueryList , which contains the set of elements.
So, when the component is initialized the component is not yet displayed until "showMe" is true. Thus, my @ViewChild references were all undefined. This is where I used @ViewChildren and the QueryList that it returns. See angular article on QueryList and a @ViewChildren usage demo.
The @ViewChild decorator allows us to inject into a component class references to elements used inside its template, that's what we should use it for. Using @ViewChild we can easily inject components, directives or plain DOM elements.
ViewChildlinkProperty decorator that configures a view query. The change detector looks for the first element or the directive matching the selector in the view DOM. If the view DOM changes, and a new child matches the selector, the property is updated.
The problem was in async data. The solution is to subscribe on QueryList changes
Example:
ngAfterViewInit(): void {
this.skillImgs.changes
.subscribe(() => console.log(this.skillImgs));
}
NOTE: You have to check whether async works properly or not. Because its hard to identify any problem if associated with async pipe. But other than it, ViewChildren works with *ngFor as shown below,
export class App {
@ViewChildren("skillImg") private skillImgs: QueryList<ElementRef>;
constructor(private renderer: Renderer) {}
ngAfterViewInit(): void {
this.skillImgs.forEach((skill: ElementRef) => {
this.renderer.setElementStyle(skill.nativeElement,"background","yellow");
});
}
}
https://plnkr.co/edit/pI35tx9gXZFO1sXj9Obm?p=preview
This issue only occurs on the dev mode. There are a number of ways by which you can handle this.
constructor(private changeDetector: ChangeDetectorRef) {
}
ngAfterViewInit() {
this.skillImgs.changes.subscribe(() => {
//make changes here
this.changeDetector.detectChanges();
})
}
ngAfterViewInit() {
this.skillImgs.changes.subscribe(()=>{
Promise.resolve().then(()=>{
//make changes here
});
})
}
ngAfterViewInit() {
this.skillImgs.changes.subscribe(()=>{
settimeout(()=>{
//make changes here
});
}, 0)’
}
Reference from here
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With