Компонент transclude с встроенным шаблоном

Я использую Angular 2-RC3 и имею Component и я хочу применить transclusion, просто немного по-другому. Вот мой компонент:

import { Component, Input } from '@angular/core';

@Component({
    selector: 'my-list',
    template: `<ul>
        <li *ngFor="let item of data">
            -- insert template here --
            <ng-content></ng-content>
        </li>
    </ul>`
})
export class MyListComponent {
    @Input() data: any[];
}

И я использую это так:

<my-list [data]="cars">
    <div>{{item.make | uppercase}}</div>
</my-list>

Как видите, я пытаюсь определить встроенный шаблон, который будет использоваться моим компонентом. Теперь это идет ужасно неправильно. Во-первых, исключение привязки данных говорит это can't read property 'make' of undefined, Пытается читать item.make от моего окружающего компонента, а не от MyListComponent, Но даже если я временно отключу это сейчас:

<my-list [data]="cars">
    <div>{item.make | uppercase}</div>
</my-list>

Тогда появляется вторая проблема:

-- insert template here --
-- insert template here --
-- insert template here --
-- insert template here --
{item.make | uppercase}

Таким образом, Angular на самом деле не копирует шаблон для использования в *ngFor, он просто связывает элементы, и они в конечном итоге связываются с последним элементом.

Как мне заставить это работать?

У меня была такая же проблема с AngularJS, где petebacondarwin опубликовал решение для манипулирования DOM с помощью компиляции, и это было здорово. У меня есть этот вариант с Angular 2, а также путем инъекций ElementRef в моем компоненте, но! Одно большое отличие состоит в том, что compile в AngularJS отключился до привязки данных, что означает отсутствие проблем с использованием {{item.make}} в шаблоне. С Angular 2 это, кажется, не пойдет, так как {{item}} разбирается заранее. Так каков наилучший способ сделать это? Используя немного другое обозначение [[item]] и строка, заменяющая всю вещь, не кажется самым элегантным способом...

Заранее спасибо!

// Редактировать: вот Plnkr, который воспроизводит проблему.

2 ответа

Решение

Разобрать ngForTemplate метод:

(Начиная с Angular 4, элемент теперь называется <ng-template>.)

  1. Использовать <template> тег как во внешнем, так и во внутреннем компонентах, вместо <ng-content>,

  2. <li> перемещается в html app.component, а <template> в этом компоненте есть специальный атрибут let-, который ссылается на итеративную переменную во внутреннем компоненте:

    <my-list [data]="cars">
      <template let-item>
        <li>
          <div>{{item.make | uppercase}}</div>
        </li>
      </template>
    </my-list>
    
  3. Внутренний компонент имеет <template> а так же использует вариант ngFor так:

    <ul>
        <template #items ngFor [ngForOf]="data" [ngForTemplate]="tmpl">
            -- insert template here --
        </template>
    </ul>
    
  4. Переменная 'tmpl', назначенная атрибуту ngForTemplate, должна быть извлечена в коде компонента:

    export class MyListComponent {
        @Input() data: any[];
        @ContentChild(TemplateRef) tmpl: TemplateRef;
    }
    
  5. @ContentChild и TemplateRef - угловые биты, поэтому их необходимо импортировать

    import { Component, Input, ContentChild, TemplateRef } from '@angular/core';
    

Смотрите форк вашего plunkr с этими изменениями здесь plnkr.

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

Я могу видеть, что это может быть полезно, когда итерация предоставляется во внутреннем компоненте (скажем, из вызова службы), и, возможно, выполнять некоторые манипуляции с шаблоном в коде.

  • <ng-content> внутри *ngFor не работает, поэтому

    <li *ngFor="let item of data">
        -- insert template here --
        <ng-content></ng-content>
    </li>
    

не буду делать ничего значимого. Все будет включено в первый <ng-content>

https://github.com/angular/angular/issues/8563 может решить некоторые из ваших требований.

Другие вопросы по тегам