Загрузить динамический компонент в * ngIf - createComponent не определен - PullRequest
0 голосов
/ 31 октября 2019

Я пытаюсь загрузить динамический компонент в мое представление, когда пользователь нажимает кнопку, которая выглядит следующим образом:

<button (click)="showContent()">Show Panel</button>
<!--This starts as 'showPanel = false'-->
<div *ngIf="showPanel">
    <ng-template #ref></ng-template>
</div>

Однако, когда я нажимаю кнопку в моем представлении, я пытаюсь запуститьloadComponent() но я получаю сообщение об ошибке Cannot read property 'createComponent' of undefined

Я искал некоторые решения для этого, были некоторые предложения использовать QueryList<ViewContainerRef>, но мне не удалось заставить его работать.

Источник: ViewContainerRef не определен при вызове в ngAfterViewInit

В другом решении было предложено использовать ElementRef и проверять состояние изменения, но даже это быловсегда неопределен при попытке проверить его в ngAfterViewInit.

Источник: @ ViewChild in * ngIf

Я ищу варианты, которыебудет работать с Angular 8, но я не совсем уверен, где искать дальше.

Код ниже:

parent.component.ts

export class ParentComponent {

@ViewChild('ref', { static: false, read: ViewContainerRef }) ref: ViewContainerRef;

showPanel = false;

loadComponent(): void {
    const factory = this.componentFactoryResolver.resolveComponentFactory(ChildComponent);
    const component = this.ref.createComponent(factory);
    component.changeDetectorRef.detectChanges();
}

showContent(): void {
    this.showPanel = !this.showPanel;
    this.loadContent(); // Error is thrown here, this doesn't make sense as *ngIf should now be true.
}

Ответы [ 2 ]

1 голос
/ 01 ноября 2019

Следуя идеям Юрзуи, вот как я могу их применить:

Здесь - это пример StackBlitz .

changeDetectorRef.detectChanges()

showContent () {
    this.showPanel = !this.showPanel;

   if (this.showPanel) {
      this.cdr.detectChanges();
      this.loadComponent();
    }
  }

сеттер для ViewChild

  private _ref: ViewContainerRef;

  private get ref () {
    return this._ref;
  }

  @ViewChild('ref', { static: false, read: ViewContainerRef })
  private set ref (r) {
    console.log('setting ref', r)
    this._ref = r;

    if (this._ref) {
      this.loadComponent();
    }
  }

  showPanel = false;

  constructor (
    private cdr: ChangeDetectorRef,
    private cfr: ComponentFactoryResolver,
  ) { }

  loadComponent () {
    const factory = this.cfr.resolveComponentFactory(ChildComponent);
    const component = this.ref.createComponent(factory);
  }

  showContent () {
    this.showPanel = !this.showPanel;
  }

с использованием <ng-container>

Как вы указали, использование ngTemplateOutlet обычно является хорошим решением, нокогда вы имеете дело с более чем одним компонентом, выполнение всей этой логики в шаблоне может быть затруднительным.

Мы можем использовать API ViewContainerRef для обработки всего из вашего компонента(.ts file).

<button (click)="showContent()">Show Panel</button>

<ng-container #vcr></ng-container>
@ViewChild('vcr', { static: true, read: ViewContainerRef })
 vcr: ViewContainerRef;

 showContent () {
  this.showPanel = !this.showPanel;  

  this.showPanel && this.attachComponent();

  !this.showPanel && this.removeComponent();  
}

private attachComponent () {
  const compFactory = this.cfr.resolveComponentFactory(ChildComponent);

  const compView = this.vcr.createComponent(compFactory);
}

private removeComponent () {
    this.vcr.clear();
}

Этот подход дает вам больший контроль, чем вы можете справиться!
Например, вы можете сохранить состояние компонента после того, как showPanel станет ложным с помощьюиспользуя vcr.detach и vcr.insert.

Вы можете найти, как прямо здесь .

0 голосов
/ 31 октября 2019

Это потому, что * ngIf удаляет элемент div, в то время как условие оценивается как ложное, это означает, что дочерний элемент не существует внутри шаблона вашего компонента.

вы можете использовать вместо него [hidden], который только скрываетэлемент div, чтобы вы могли получить к нему доступ через переменную ссылки на шаблон.

<button (click)="showContent()">Show Panel</button>
<!--This starts as 'showPanel = false'-->
<div [hidden]="!showPanel">
    <ng-template #ref></ng-template>
</div>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...