Как динамически загружать несколько шаблонов ng с помощью ngFor на основе свойства объекта

[Шаг 1] У меня есть объект «статья», содержащий массив из нескольких элементов контента. В зависимости от типа элемента (например, заголовок/изображение) должен отображаться другой шаблон. Я не хочу явно иметь собственный компонент для этого, если в этом нет необходимости, поэтому я предполагаю, нужен ли ViewChild или ContentChild?

Я изучил все вопросы, связанные с ngTemplate, но пока не нашел решения. Я попытался переписать https://stackblitz.com/edit/angular-q1y2vz из ngTemplateOutlet с динамическим значением, но пока не удалось.

component.ts

articles: any[] = [ {
    "id_seq": "1234",
    "content":[
      {
        "id": "123456",
        "order": 0,
        "type": "header",
        "data":
          {
            "text": "loremipsum"
          }
      },
      {
        "id": "234567",
        "order": 1,
        "type": "image",
        "data":
          {
            "url": "http://www.google.de/image.jpg"
          }
      }]
  }]

component.html

<ng-container *ngFor="let content of articles[0]?.content;">
  <ng-container [ngTemplateOutlet]="content?.type">
  </ng-container>
</ng-container>


<ng-template #header >
  <p>I'm a header</p>
</ng-template>

<ng-template #image >
  <p>I'm an image</p>
</ng-template>

В зависимости от синтаксиса и размещения кода ngTemplateOutlet я получаю сообщение об ошибке

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

или вообще ничего не отображается.

Если я наберу статическую строку, например «заголовок», она сработает. Итак, я предполагаю, что content?.type не подходит. С другой стороны, что-то вроде этого отлично работает:

<accordion>
  <accordion-group heading="articlecontent" [isOpen]="isFirstOpen">
    <div class="accordion_no_side_margin">
      <accordion-group *ngFor="let content of articles[0]?.content"
                       [heading]="content?.type">
        {{ content?.id }}
        {{ content?.order }}
        {{ content?.type }}
        {{ content?.data }}
      </accordion-group>
    </div>
  </accordion-group>
</accordion>

[Шаг 2] В конечном итоге я хочу объединить оба фрагмента кода, чтобы ng-шаблон выполнялся внутри аккордеона.

[Шаг 3] Позже я намерен добавить ngTemplateOutletContext и предоставить некоторую информацию, но другие шаги должны работать без этого.

На данный момент я использую @angular-devkit/core 8.1.3.


person connectedMind    schedule 02.10.2019    source источник


Ответы (2)


Поскольку по какой-то причине он просто не хотел работать с ngFor, я попробовал ngSwitchCase, который работает достаточно хорошо и с тех пор ни разу не оглядывался назад (хотя было бы неплохо иметь шаблоны/ng-контейнеры снаружи)

<ng-container *ngFor="let content of localArticles[0]?.content">
  <ng-container [ngSwitch]="content?.type">

    <ng-container *ngSwitchCase="'header'">
      <p>I'm a header</p>
    </ng-container>

    <ng-container *ngSwitchCase="'image'">
      <p>I'm an image</p>
    </ng-container>

  </ng-container>
</ng-container>
person connectedMind    schedule 15.10.2019

Не знаю, что происходит с articles[0]?.content в вашем коде, но попробуйте так:

<ng-container *ngFor="let content of articles[0]?.content;">
    <ng-container *ngIf="content?.type == header; then header; else image"></ng-container>
    <ng-template #header >
      <p>I'm a header</p>
    </ng-template>

    <ng-template #image >
      <p>I'm an image</p>
    </ng-template>
  </ng-container>

попробуйте так, если выше не работает. Ваш массив не загружается:

<div *ngIf="articles | async as artic">
  <ng-container *ngFor="let article of artic">
    <ng-container *ngfor="let ar of article.content">

      <ng-container *ngIf="ar.type == header; then header; else image"></ng-container>
      <ng-template #header>
        <p>I'm a header</p>
      </ng-template>

      <ng-template #image>
        <p>I'm an image</p>
      </ng-template>
    </ng-container>
  </ng-container>
</div>

Если у вас есть жестко закодированный массив, вы можете удалить | async канал.

Ответьте на то, что Вы сказали мне в комментариях:

<div *ngIf="articles | async as artic">
  <ng-container *ngFor="let article of artic">
    <ng-container *ngfor="let ar of article.content">

      <ng-container *ngIf="ar.type == 'header'"><p>I'm a header</p></ng-container>
      <ng-container *ngIf="ar.type == 'image'"><p>I'm a header</p></ng-container>
      <ng-container *ngIf="ar.type == 'pain in ass'"><p>I'm a header</p></ng-container>
      <ng-container *ngIf="ar.type == 'love masturbating'"><p>I'm a header</p></ng-container>
      <ng-container *ngIf="ar.type == 'love dogs'"><p>I'm a header</p></ng-container>

    </ng-container>
  </ng-container>
</div>
person Mises    schedule 02.10.2019
comment
Интересный подход, такого еще не видел. Но не работает к сожалению. article[0]?.content — это просто обходной путь, потому что у меня еще нет определенной модели статьи, а в локальном массиве есть только одна статья. - person connectedMind; 02.10.2019
comment
Я вижу, что вы там сделали (по крайней мере, частично). Дело в том, что у меня более 10 типов (заголовок и изображение приведены только для примера), поэтому использование if/else на самом деле не вариант. Также какой смысл использовать ngFor 2 раза...? - person connectedMind; 02.10.2019
comment
Потому что у вас есть массив в массиве? В этом смысл 2x ngFor. - person Mises; 02.10.2019
comment
Если у вас более 10 типов, просто используйте *ngIf для каждого типа. Как *ngIf="type == header" следующий *ngIf="type == image" и т.д. - person Mises; 02.10.2019