问题
I'm trying to execute a function of a child component with a push of a button on the parent but for some reason its undefined.
Parent:
.. com1.html
<ng-template #content let-c="close" let-d="dismiss">
<div class="modal-header">
<h4 class="modal-title">Title</h4>
</div>
<div class="modal-body" style="display: flex; justify-content: center;">
<app-com2 [var1]="value" #traceRef></app-com2>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-dark" (click)="savePNG()"><i class="fa fa-download"></i>PNG</button>
<button type="button" class="btn btn-outline-dark" (click)="c()">Close</button>
</div>
</ng-template>
<button class="btn btn-sm btn-secondary" (click)="open(content)">Button</button>
...com1.ts
export class com1 implements OnInit {
@ViewChild('traceRef') traceRef;
value = "something";
constructor(private modalService: NgbModal) {}
savePNG() {
this.traceRef.savePNG();
}
open(content: any) {
this.modalService.open(content, { windowClass: 'temp-modal' });
}
}
Could anyone point me in the right directions cuz the other threads were of no help :( Even with ngAfterViewInit its still undefined.
回答1:
The reason why it is not working as it would be in common scenario with ng-template
is that NgbModal
service creates template and doesn't attach it to parent view but rather to root ApplicationRef
const viewRef = content.createEmbeddedView(context);
this._applicationRef.attachView(viewRef);
https://github.com/ng-bootstrap/ng-bootstrap/blob/0f8055f54dad84e235810ff7bfc846911c168b7a/src/modal/modal-stack.ts#L107-L109
This means your query will be always undirty for your component.
In common cases(see example provided by @ConnorsFan) we use vcRef.createEmbeddedView
which is responsible for checking queries in parent view:
vcRef.createEmbeddedView
||
\/
function attachEmbeddedView(
...
Services.dirtyParentQueries(view); <========== set dirty for queries
https://github.com/angular/angular/blob/0ebdb3d12f8cab7f9a24a414885ae3c646201e90/packages/core/src/view/view_attach.ts#L12-L23
The simple solution i can see here is just pass or call reference directly in template:
(click)="traceRef.savePNG()"
回答2:
I think you need something as below. replace ChildComponent with your child class name
@ViewChild('traceRef')
traceRef:ChildComponent
回答3:
I notice that your parent template is a modal, which you open using a template variable here: ((click)="open(content)"
via your modal service. So this means it gets added to the DOM dynamically.
So this could be a timing issue because the parent is loaded dynamically.
I would try this, in the ngAfterViewInit of the CHILD, fire an event using EventEmitter. Grab the event in the parent, and then set a flag to true. Then IN THEORY you can do this in the parent:
savePNG() {
if (this.childLoaded) this.traceRef.savePNG();
}
You know for sure the child is loaded and so savePNG is available. However, see my note - traceRef will still be undefined.
So you need to pass a reference to the child function in the EventEmitter and use that instead of #traceRef.
Eg - in the child:
export class app-com2 implements blah blah {
@Output() childLoaded: EventEmitter<any> = new EventEmitter<any>();
savePNG() {
// code to save the image
}
ngAfterViewInit() {
this.childLoaded.emit(this.savePNG);
}
}
Parent:
.. com1.html
<ng-template #content let-c="close" let-d="dismiss">
<div class="modal-header">
<h4 class="modal-title">Title</h4>
</div>
<div class="modal-body" style="display: flex; justify-content: center;">
<app-com2 [var1]="value" (childLoaded)="handleChildLoaded($event)"></app-com2>
</div>
..com1.ts
handleChildLoaded(save: any) {
const result = save;
console.log(result);
}
Docs on child to parent comms.
NOTE
Thinking about this more - this is a lifecycle/timing issue, so your code will never work. This might be why:
Your parent component loads, fires all lifecycle hooks (eg. ngOnInit and ngAfterViewInit etc). #content gets processed and becomes available in ngAfterViewInit and later hooks.
You now click a button to load #content (which is available). But it has
<app-com2>
component, which is referenced with the #traceRef template variable. But template variables are only processed just before the ngAfterViewInit hook fires - and that has already fired for the parent, and so #traceRef arrives too late for Angular to hook it up. Hence it will always be undefined.
See comments below regarding (2) - not quite right, but I suspect it is because of the way you are loading the modal that #traceRef will always be undefined.
来源:https://stackoverflow.com/questions/50353977/viewchild-returns-undefined-angular2