Визуализировать динамический шаблон на основе типа

Мне очень нравится ответ, предоставленный для динамического шаблона, основанного на значении, а не на переменной с ngTemplateOutlet. Однако я не могу заставить его работать. Упрощенный пример:

export class A {
}

export class MyComponent 
  implements OnInit {

  public controls$ = Observable<any[]>([]);

  ngOnInit() {
    this.controls$.next([new A()]);
  }

  public getTypeName(control: any) {
    if (control instanceof A) {
      return "AControl";
    }
    return "";
  }
}

Шаблон:

<div *ngFor="let control of control$ | async">
  {{ getControlType(control) }}
</div>

Урожайность:

AControl

Все идет нормально. При добавлении шаблона получаю исключение:

<div *ngFor="let control of control$ | async">
  {{ getControlType(control) }}
  <ng-container 
    [ngTemplateOutlet]="getControlType(control)"
    [ngTemplateOutletContext]="{ control: control }">
  </ng-container>
</div>

<ng-template 
  #AControl 
  let-item="control">A Control</ng-template>

бросает:

templateRef.createEmbeddedView не является функцией

Я не уверен, что мне нужно изменить, чтобы шаблон #AControl отображался в контейнере.


person Erik Philips    schedule 28.10.2019    source источник


Ответы (2)


похоже, что виноват [ngTemplateOutlet]="getControlType(control)". Я не вижу кода для getControlType(), но полагаю, что он возвращает строку s, тогда как это должен быть объект TemplateRef. Вызов функций в шаблоне не является хорошей идеей, если вы не используете ChangeDetectionStrategy.OnPush, поэтому я предлагаю вместо этого использовать оператор switch в вашем шаблоне. Тем не менее, с

@ViewChild("AControl", {static: true})
AControl: TemplateRef<any>;

вы можете получить доступ к шаблону в вашем файле .ts и вернуться из функции getControlType

person D Pro    schedule 28.10.2019
comment
Если элементы управления являются динамическими и могут повторяться, как мне динамически создать TemplateRef для каждого элемента? (Может быть 10 AControl (s)) - person Erik Philips; 28.10.2019

С помощью ответа D Pro я обнаружил, что мне действительно не нужно использовать ngTemplateOutlet в моем экземпляре (кажется, быть действительно сложным для чего-то столь же простого, как я использовал).

Мое окончательное решение было:

машинопись

export class A {
}

export class B {
}


export class MyComponent 
  implements OnInit {

  public controlsTypes$ = Observable<any[]>([]);

  ngOnInit() {
    var value1 = new A();
    var value2 = new B();
    this.controls$.next([
      { control: value1, type: getTypeName(value1)},
      { control: value2, type: getTypeName(value2)},
    ]);
  }

  public getTypeName(control: any) {
    if (control instanceof A) {
      return "AControl";
    } else if (control instanceof B) {
      return "BControl";
    }
    return "";
  }

  public onClick(control: any) {
  }
}

html:

<div *ngFor="let controlType of controlType$ | async"
     [ngSwitch]="controlType.type">
  <ng-template ngSwitchCase="AControl">
    <div (click)="onClick(controlType.control)">{{ controlType.type }}</div>
  </ng-template>
  <ng-template ngSwitchCase="BControl">
    <div (click)="onClick(controlType.control)">{{ controlType.type }}</div>
  </ng-template>
</div>

Отображает как AControl, так и BControl, и при нажатии каждого из них созданный класс передается в onClick().

person Erik Philips    schedule 28.10.2019