ViewChild возвращает «неопределенное» - Angular2 - PullRequest
0 голосов
/ 15 мая 2018

Я пытаюсь выполнить функцию дочернего компонента нажатием кнопки на родительском элементе, но по какой-то причине он не определен.

Родитель:

.. 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 голосов
/ 15 мая 2018

Я заметил, что ваш родительский шаблон является модальным, который вы открываете с помощью переменной шаблона здесь: ((click)="open(content)" через ваш модальный сервис.Таким образом, это означает, что он добавляется в DOM динамически.

Так что это может быть проблемой синхронизации, поскольку родительский файл загружается динамически.событие с использованием 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);
}

Документы от дочерних до родительских.

ПРИМЕЧАНИЕ

Думайте об этом подробнее - это проблема жизненного цикла / времени, поэтому ваш код никогда не будет работать.Это может быть причиной:

  1. Ваш родительский компонент загружает, запускает все хуки жизненного цикла (например, ngOnInit и ngAfterViewInit и т. Д.).#content обрабатывается и становится доступным в ngAfterViewInit и последующих хуках.

  2. Теперь вы нажимаете кнопку, чтобы загрузить #content (который доступен).Но он имеет компонент <app-com2>, на который ссылается переменная шаблона #traceRef.Но переменные шаблона обрабатываются только непосредственно перед срабатыванием ловушки ngAfterViewInit - и это уже сработало для родителя, и поэтому #traceRef приходит слишком поздно, чтобы Angular подключил его.Следовательно, он всегда будет неопределенным.

См. Комментарии ниже относительно (2) - не совсем верно, но я подозреваю, что из-за того, как вы загружаете модал, #traceRef всегда будетбыть неопределенным.

0 голосов
/ 15 мая 2018

Причина, по которой он работает не так, как в обычном сценарии с 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), мы используем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()"
0 голосов
/ 15 мая 2018

Я думаю, вам нужно что-то, как показано ниже. замените ChildComponent именем вашего дочернего класса

@ViewChild('traceRef') 
      traceRef:ChildComponent
...