admin管理员组文章数量:1305917
THE PROBLEM
So I have two Angular ponents, a parent and a child. The parent passes a custom template to the child ponent, which then hydrates the template with its own data using ngTemplateOutlet.
This works well for the most part. Unfortunately, I run into issues when trying to access the DOM elements of this parent template from the child.
If I try to access <div #container></div>
from the default child template using @ViewChild('container',{static: false})
, it gets the element without issue. When I do the same using the custom template passed in by appponent, I get the error "cannot read property 'nativeElement' of undefined".
What else do I have to do to access the DOM of my template?
Here's a Stackblitz
App.Component (Parent)
import { Component } from "@angular/core";
@Component({
selector: "my-app",
templateUrl: "./appponent.html",
styleUrls: ["./appponent.css"]
})
export class AppComponent {}
<child [customTemplate]="parentTemplate"></child>
<ng-template #parentTemplate let-context="context">
<div #container>HELLO FROM CONTAINER</div>
<button (click)="context.toggleShow()">Toggle Display</button>
<div *ngIf="context.canShow">Container contains the text: {{context.getContainerText()}}</div>
</ng-template>
THE PROBLEM
So I have two Angular ponents, a parent and a child. The parent passes a custom template to the child ponent, which then hydrates the template with its own data using ngTemplateOutlet.
This works well for the most part. Unfortunately, I run into issues when trying to access the DOM elements of this parent template from the child.
If I try to access <div #container></div>
from the default child template using @ViewChild('container',{static: false})
, it gets the element without issue. When I do the same using the custom template passed in by app.ponent, I get the error "cannot read property 'nativeElement' of undefined".
What else do I have to do to access the DOM of my template?
Here's a Stackblitz
App.Component (Parent)
import { Component } from "@angular/core";
@Component({
selector: "my-app",
templateUrl: "./app.ponent.html",
styleUrls: ["./app.ponent.css"]
})
export class AppComponent {}
<child [customTemplate]="parentTemplate"></child>
<ng-template #parentTemplate let-context="context">
<div #container>HELLO FROM CONTAINER</div>
<button (click)="context.toggleShow()">Toggle Display</button>
<div *ngIf="context.canShow">Container contains the text: {{context.getContainerText()}}</div>
</ng-template>
child.ponent (Child)
import {
Component,
ElementRef,
Input,
TemplateRef,
ViewChild
} from "@angular/core";
@Component({
selector: "child",
templateUrl: "./child.ponent.html",
styleUrls: ["./child.ponent.css"]
})
export class ChildComponent {
@Input() public customTemplate!: TemplateRef<HTMLElement>;
@ViewChild("container", { static: false })
public readonly containerRef!: ElementRef;
templateContext = { context: this };
canShow: boolean = false;
toggleShow() {
this.canShow = !this.canShow;
}
getContainerText() {
return this.containerRef.nativeElement.textContent;
}
}
<ng-container *ngTemplateOutlet="customTemplate || defaultTemplate; context: templateContext">
</ng-container>
<ng-template #defaultTemplate>
<div #container>GOODBYE FROM CONTAINER</div>
<button (click)="toggleShow()">Toggle Display</button>
<div *ngIf="canShow">Container contains the text: {{getContainerText()}}</div>
</ng-template>
MY QUESTION
How do I use @ViewChild to access this div from an outside template that updates with any changes in the DOM? (Note: Removing the *ngIf is NOT an option for this project)
What's causing this? Are there any lifecycle methods that I can use to remedy this issue?
MY HUNCH I'm guessing that ViewChild is being called BEFORE the DOM updates with its new template and I need to setup a listener for DOM changes. I tried this and failed so I'd really appreciate some wisdom on how best to proceed. Thanks in advance :)
EDIT:
This solution needs to properly display <div #container></div>
regardless of whether you're passing in a custom template or using the default one.
1 Answer
Reset to default 6ViewChild
doesn't seem to pick up a rendered template - probably because it's not part of the ponents template initially. It's not a timing or lifecycle issue, it's just never available as a ViewChild
An approach that does work is to pass in the template as content to the child ponent, and access it using ContentChildren
. You subscribe to the ContentChildren
QueryList
for changes
, which will update when the DOM element bees rendered
You can then access the nativeElement
(the div
). If you wanted you could add listeners here to the DOM element, and trigger cd.detectChanges
afterwards, but that would be a bit unusual. It would probably be better to handle DOM changes in the parent element, and pass the required values down to the child using regular @Input
on the child
@Component({
selector: "my-app",
template: `
<child>
<ng-template #parentTemplate let-context="context">
<div #container>Parent Template</div>
</ng-template>
</child>
`,
styleUrls: ["./app.ponent.css"]
})
export class AppComponent {}
@Component({
selector: "child",
template: `
<ng-container *ngTemplateOutlet="customTemplate"> </ng-container>
`,
styleUrls: ["./child.ponent.css"]
})
export class ChildComponent implements AfterContentInit {
@ContentChild("parentTemplate")
customTemplate: TemplateRef<any>;
@ContentChildren("container")
containerList: QueryList<HTMLElement>;
ngAfterContentInit() {
this.containerList.changes.subscribe(list => {
console.log(list.first.nativeElement.innerText);
// prints 'Parent Template'
});
}
}
本文标签: javascriptHow do I use ViewChild with an external ngtemplate (Angular 11)Stack Overflow
版权声明:本文标题:javascript - How do I use @ViewChild with an external ng-template (Angular 11) - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741815013a2399023.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论