Компонент 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>
.)
Использовать
<template>
тег как во внешнем, так и во внутреннем компонентах, вместо<ng-content>
,<li>
перемещается в html app.component, а<template>
в этом компоненте есть специальный атрибут let-, который ссылается на итеративную переменную во внутреннем компоненте:<my-list [data]="cars"> <template let-item> <li> <div>{{item.make | uppercase}}</div> </li> </template> </my-list>
Внутренний компонент имеет
<template>
а так же использует вариант ngFor так:<ul> <template #items ngFor [ngForOf]="data" [ngForTemplate]="tmpl"> -- insert template here -- </template> </ul>
Переменная 'tmpl', назначенная атрибуту ngForTemplate, должна быть извлечена в коде компонента:
export class MyListComponent { @Input() data: any[]; @ContentChild(TemplateRef) tmpl: TemplateRef; }
@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 может решить некоторые из ваших требований.
- Связывание событий при использовании ngForTemplate в Angular 2
- Привязанная переменная ng-content select может быть подходом, который позволит сделать нечто подобное, как вы продемонстрировали в своем вопросе.
Это требует от пользователя вашего компонента, чтобы обернуть содержимое в<template>
тег.