ViewChild返回' undefined' - Angular2

时间:2018-05-15 15:27:33

标签: angular viewchild

我试图通过按下父级按钮来执行子组件的功能,但由于某种原因它未定义。

父:

.. 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' });
  }
}

任何人都可以指出我正确的方向因为其他线程没有帮助:( 即使使用ngAfterViewInit,它仍然未定义。

3 个答案:

答案 0 :(得分:2)

它与ng-template的常见情况不同的原因是NgbModal服务创建模板并且不将其附加到父视图,而是附加到根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

这意味着您的组件的查询总是无效。

在一般情况下(参见@ConnorsFan提供的example),我们使用vcRef.createEmbeddedView负责检查父视图中的查询:

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

我在这里看到的简单解决方案就是直接在模板中传递或调用引用:

(click)="traceRef.savePNG()"

答案 1 :(得分:0)

我认为您需要以下内容。将ChildComponent替换为您的子类名

@ViewChild('traceRef') 
      traceRef:ChildComponent

答案 2 :(得分:0)

我注意到您的父模板是一个模态,您可以使用模板变量在此处打开:((click)="open(content)"通过模态服务。所以这意味着它会动态地添加到DOM中。

因此,这可能是一个时间问题,因为父项是动态加载的。

我会尝试这个,在CHILD的ngAfterViewInit中,使用EventEmitter触发一个事件。抓取父项中的事件,然后将标志设置为true。然后在理论中,您可以在父母中执行此操作:

  savePNG() {
    if (this.childLoaded) this.traceRef.savePNG();
  }

您确定孩子已加载,因此savePNG可用。但是,请参阅我的注释 - traceRef仍然是未定义的。

因此,您需要在EventEmitter中传递对子函数的引用,并使用它而不是#traceRef。

例如 - 在孩子身上:

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);
  }
}

父:

.. 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关于孩子到父母的通讯。

请注意

更多地考虑这个问题 - 这是一个生命周期/计时问题,因此您的代码将无法运行。这可能就是原因:

  1. 您的父组件加载,触发所有生命周期挂钩(例如ngOnInit和ngAfterViewInit等)。 #content得到处理,并在ngAfterViewInit和以后的钩子中可用。

  2. 现在单击按钮加载#content(可用)。但它有<app-com2>组件,用#traceRef模板变量引用。但是模板变量只在ngAfterViewInit钩子触发之前处理 - 而且已经为父节点触发了,因此#traceRef来得太晚,Angular无法将它连接起来。因此它总是未定义的。

  3. 请参阅下面关于(2)的评论 - 不太正确,但我怀疑是因为您加载模式的方式,#traceRef将始终未定义。