【问题标题】:Angular 6 nested ViewChild inside ng-template is nullng-template 中的 Angular 6 嵌套 ViewChild 为空
【发布时间】:2019-04-11 22:25:41
【问题描述】:

我们在应用程序中使用了模式 (ng-bootstrap's one)。该模态看起来像:

<ng-template #modal let-modal>
    <app-audio #audio></app-audio>
</ng-template>

这是逻辑:

@ViewChild('modal')
modal: ElementRef;

@ViewChild('audio')
audio: AudioComponent;

模态打开方式:

this.modalService.open(this.modal, { size: 'lg' });

到这里为止一切都很好。模态打开并显示音频组件。但是现在,我们想要访问组件内部的逻辑,并且在执行以下操作时:

this.audio.somePublicComponentFunction()

恰好 this.audio 为空。我已经尝试让孩子使用 angular 的变化检测器,但找不到将 this.audio 与实际组件正确链接的方法。有任何想法吗?非常感谢。

You can see the issue here: stackblitz.com/edit/angular-ofmpju

【问题讨论】:

  • 请提供minimal reproducible example 重现您的问题。
  • 嗨@trichetriche,就这么简单。即使在打开模式并呈现内容后,我也无法引用音频组件。将 app-audio 留在模态之外时,一切正常。
  • 我了解您的问题。我要求您在沙盒中提供一个示例。
  • 不确定这是否有帮助。不过,还是想和大家分享一下。 stackoverflow.com/questions/41669787/…
  • 抱歉@trichetriche,今天很忙。我在这里重现了这个问题:stackblitz.com/edit/angular-ofmpju

标签: javascript angular ng-bootstrap viewchild


【解决方案1】:

你可以像这样在没有引用变量的情况下读取子组件

@ViewChild(AudioComponent)
audio: AudioComponent;

这将为您提供子组件的实例 - 您可以在其中访问方法

this.audio.someComponentFunction()

你的html

<ng-template #modal let-modal>
    <app-audio></app-audio>
</ng-template>

我认为这将解决您的问题 - 快乐编码

更新:

希望我找到了解决此问题的方法 - 如果您只想触发一个功能,您可以使用此方法

我刚刚添加了一个带有gettersetter的属性,并在我们set的值时触发了函数

@Input()
  get triggerFunction(): boolean {
    return this.runFuntion;
  }

  set triggerFunction(value: boolean) {
    this.runFuntion = value;
    this.someFunction();
  }  

所以这会导致每次模型出现时都会触发该函数 - 上面提到的属性属于嵌套在&lt;ng-template&gt; 内的子组件,所以最终模型模板将如下所述:

<ng-template #modal let-modal>
  <app-audio [triggerFunction]="true"></app-audio>
</ng-template>

希望这暂时可以解决问题 - 谢谢

【讨论】:

  • 当模板中有多个 app-audio 标签时,这可能是个问题。这就是我们有模板引用的原因。来到您的解决方案,这与使用模板参考有何不同?他们不是都一样吗?
  • 你能在那种情况下使用 ViewChildren() 然后@AmitChigadani 吗?
  • 你好拉胡尔,感谢你的想法。不幸的是,它仍然无法正常工作。我认为问题来了,因为模态内容在模态本身打开之前不会呈现。由于 ViewChild 属性是在 ngAfterViewInit 之前设置的,并且我们的模态是由用户手动打开的,由于某种原因,更改检测器无法在此处获取更改,并且 this.audio 仍然为空。
  • @AmitChigadani @ViewChild('audio', { observe: AudioComponent }) 不,它们不一样,默认情况下查看子级检查 HTML 标记。
  • @AmitChigadani 模板引用在页面加载时将不可用 - 因为它已在 &lt;ng-template&gt; 内,所以当您直接阅读可能有效的组件时 - 似乎是一个不确定的想法- 它在一种情况下对我有用
【解决方案2】:

您可以从模板本身调用方法audio.someFunction()

<ng-template #modal let-modal>
  <div style="background-color: red;"> 
  <h1>Modal header</h1>
  <app-audio #audio></app-audio>
  <!-- on click, call audio comp method someFunction() using its reference --> 
  <button (click)="audio.someFunction()">Operate with audio from inside modal</button>
  </div>
</ng-template>

这里不需要@ViewChild 属性。这应该为您解决问题。

分叉demo

【讨论】:

  • 谢谢,这是帮助我找出最终解决方案的原因。我为模态组件创建了一个名为 setAudioComponent 的方法,用户通过与模态交互调用该方法。此方法接收#audio 组件并设置私有属性。
【解决方案3】:

对我来说,所有这些解决方案都不起作用,我仍然想在第三方 ng-template 中访问我自己的组件。这是我的“解决方案”。我认为这不是最佳实践,而是获得我想要的绝望的解决方案;-) 当然,它只适用于您自己的组件。

// mycomponent.ts => component that needs to be accessed 
import { Component, Output, EventEmitter, AfterViewInit } from '@angular/core';

@Component({
    selector: 'my-component',
    templateUrl: './mycomponent.html'
})
export class MyComponent implements AfterViewInit {
   @Output() initialized: EventEmitter<MyComponent> = new EventEmitter<MyComponent>();

   ngAfterViewInit(): void {
        this.initialized.emit(this);
   }

   reload(): void {
        // Do something
   }
}


// somecomponent.html => component with <ng-template> holding MyComponent

<ng-template>
    <div class="btn-group ml-2">
      <my-component (initialized)="onMyComponentInitialized($event)"></my-component>
    </div>
</ng-template>


// somecomponent.ts => component with <ng-template> holding MyComponent
import { Component, OnDestroy } from '@angular/core';
import { MyComponent } from '../../my-component';

@Component({
    selector: 'some-component',
    templateUrl: './some-component.html'
})
export class SomeComponent implements OnDestroy {
    private _myComponent: MyComponent = null;

    onMyComponentInitialized(component: MyComponent) {
        this._myComponent = component;
    }

    someOtherMethod() {
        if (this._myComponent) {
            // Call some method on the component
            this._myComponent.reload();
        }
    }

    ngOnDestroy() {
        this._myComponent = null;
    }
}

【讨论】:

  • 在某些情况下,我认为这是一个完全合理的方法。尤其是在最初实例化宿主组件时未呈现模板内的内容时。这是一种更被动的方法。
猜你喜欢
  • 2021-07-25
  • 1970-01-01
  • 1970-01-01
  • 2017-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-03
相关资源
最近更新 更多