Angular 6 вложенный ViewChild внутри ng-template имеет значение null

Мы используем модальное окно (один из ng-bootstrap) в нашем приложении. Этот модал выглядит так:

<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 имеет значение null. Я уже пытался получить ребенка с детектором изменений angular, но не могу найти способ правильно связать this.audio с фактическим компонентом. Любые идеи? Большое спасибо.

Вы можете увидеть проблему здесь: stackblitz.com/edit/angular-ofmpju


person Erwol    schedule 08.11.2018    source источник
comment
Предоставьте минимально воспроизводимый пример, воспроизводящий вашу проблему.   -  person    schedule 08.11.2018
comment
Привет @trichetriche, это так просто. Я не могу ссылаться на аудиокомпонент даже после открытия модального окна и отображения его содержимого. При выходе app-audio за пределы модального окна все работает нормально.   -  person Erwol    schedule 08.11.2018
comment
Я понял вашу проблему. Я прошу вас привести пример в песочнице.   -  person    schedule 08.11.2018
comment
Не уверен, полезно это или нет. Но, думал поделиться с вами в любом случае. stackoverflow.com/questions/41669787/   -  person Anjana Silva    schedule 08.11.2018
comment
Извини, @trichetriche, сегодня напряженный день. Я воспроизвел проблему здесь: stackblitz.com/edit/angular-ofmpju   -  person Erwol    schedule 08.11.2018


Ответы (3)


Вы можете прочитать дочерний компонент без переменной refrence, как это

@ViewChild(AudioComponent)
audio: AudioComponent;

Это даст вам экземпляр дочернего компонента, где вы можете получить доступ к методу

this.audio.someComponentFunction()

Ваш html

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

Я думаю, это решит вашу проблему - удачного кодирования

Обновление:

Надеюсь, я нашел обходной путь для этой проблемы - если вы хотите вызвать только одну функцию, вы можете использовать этот метод.

Я только что добавил свойство с getter и setter и активировал функцию, когда мы set значение

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

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

Таким образом, это приводит к запуску функции каждый раз, когда появляется модель - упомянутое выше свойство принадлежит дочернему компоненту, который вложен внутри <ng-template>, поэтому, наконец, шаблон модели будет читаться, как указано ниже:

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

Надеюсь, что на данный момент это будет обходным путем. Спасибо.

person Rahul    schedule 08.11.2018
comment
Это может быть проблемой, если в шаблоне несколько тегов app-audio. Вот почему у нас есть ссылка на шаблон. И что касается вашего решения, чем оно отличается от использования ссылки на шаблон? Разве они не одинаковы? - person Amit Chigadani; 08.11.2018
comment
Можете ли вы использовать ViewChildren() в этом случае, а затем @AmitChigadani? - person rrd; 08.11.2018
comment
Привет, Рахул, и спасибо за твою идею. К сожалению, он все еще не работает. Я думаю, что проблема возникает из-за того, что модальное содержимое не отображается до тех пор, пока не будет открыто само модальное окно. А так как свойства ViewChild устанавливаются перед ngAfterViewInit, а наш модал открывается пользователем вручную, по какой-то причине детектор изменений не получает здесь изменений, и this.audio по-прежнему равно нулю. - person Erwol; 08.11.2018
comment
@AmitChigadani @ViewChild('audio', { observe: AudioComponent }) и нет, это не одно и то же, дети по умолчанию проверяют HTML-тег. - person ; 08.11.2018
comment
Ссылка на шаблон @AmitChigadani будет недоступна при загрузке страницы - поскольку она была внутри <ng-template>, поэтому, когда вы читаете компонент напрямую, который может работать - кажется, идея не уверена в этом - у меня это сработало в одном сценарии - person Rahul; 08.11.2018
comment
@trichetriche Он все еще использует @ViewChild с именем компонента, а не ViewChildren, что аналогично использованию @ViewChild со ссылкой на шаблон. - person Amit Chigadani; 08.11.2018
comment
@AmitChigadani @ViewChild('audio', { observe: AudioComponent }) использует имя переменной шаблона, так что нет, вы больше не используете имя компонента. Вы используете класс, чтобы явно указать, что наблюдать. - person ; 08.11.2018
comment
Согласен, но в ответе выше ссылка на шаблон не используется. Это то, что я указываю здесь. - person Amit Chigadani; 08.11.2018
comment
Ну, это не имеет значения, поскольку у OP нет нескольких экземпляров, и я все равно отвечал вам о том, как использовать имя переменной. - person ; 08.11.2018
comment
Привет @RahulSwamynathan, я обновил свой вопрос примером кода, если вы хотите его проверить. Спасибо. - person Erwol; 08.11.2018
comment
@Erwol Я обновил ответ с некоторыми изменениями - пожалуйста, посмотрите - person Rahul; 08.11.2018

Вы можете вызвать метод 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. Это должно помочь вам.

Разветвленная демонстрация

person Amit Chigadani    schedule 08.11.2018
comment
Спасибо, это то, что помогло мне найти окончательное решение. Я создал метод для модального компонента с именем setAudioComponent, который вызывается пользователем при взаимодействии с модальным компонентом. Этот метод получает компонент #audio и устанавливает приватное свойство. - person Erwol; 08.11.2018

Для меня все эти решения не сработали, и я все еще хотел получить доступ к своему собственному компоненту внутри стороннего ng-шаблона. Вот мое «решение». Я не думаю, что это лучшая практика, но отчаянное решение получить то, что я хочу ;-) Конечно, это работает только для ваших собственных компонентов.

// 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;
    }
}
person Sander Oosterwijk    schedule 09.10.2019