我们说我有这个简单的列表呈现组件:
import {Input, Component } from 'angular2/core'
@Component({
selector: 'my-list',
template: `
<div *ngFor='#item of items' (click)='onItemClicked(item)'>
{{item}}
</div>
`
})
class MyList {
@Input() items: string[];
onItemClicked(item) { console.log('Item clicked:', item); }
}
我这样用:
<my-list [items]='myAppsItems'></my-list>
到目前为止一切顺利。
接下来我决定我希望用户能够为渲染的项目提供自己的模板,所以我更改了组件
@Component({
selector: 'my-list',
template: `
<template ngFor [ngForOf]="items" [ngForTemplate]="userItemTemplate" (click)='onItemClicked(item)'>
</template>
`
})
class MyList {
@Input() items: string[];
@ContentChild(TemplateRef) userItemTemplate: TemplateRef;
onItemClicked(item) { console.log('Item clicked:', item); }
}
并像这样使用它:
<my-list [items]='items'>
<template #item>
<h1>item: {{item}}</h1>
</template>
</my-list>
这只适用于我没有将任何事件处理程序绑定到列表项(plunker)。如果我尝试绑定到click事件,就像我在组件的第一个版本中所做的那样,Angular会抛出以下异常:
"Event binding click not emitted by any directive on an embedded template"
这里是plunker showing that。您可以删除点击绑定,它可以正常工作。
我该如何解决这个问题?我只是希望用户能够为我将要通过ngFor迭代的从属项目指定模板,但我需要能够将处理程序绑定到这些项目。
答案 0 :(得分:15)
现在正在寻找一个星期的答案,我终于找到了一个相当不错的解决方案。我建议使用ngTemplateOutlet而不是使用ngForTemplate。
这里已经很好地描述了: angular2 feeding data back to `<template>` from `[ngTemplateOutlet]`
列表项的自定义模板放在组件标记之间:
<my-list>
<template let-item="item">
Template for: <b>{{item.text}}</b> ({{item.id}})
</template>
</my-list>
组件模板:
<ul>
<li *ngFor="let item of listItems" (click)="pressed(item)">
<template
[ngTemplateOutlet]="template"
[ngOutletContext]="{
item: item
}">
</template>
</li>
</ul>
我在这里做了一个例子:https://plnkr.co/edit/4cf5BlVoqzZdUQASVQaC?p=preview
答案 1 :(得分:10)
项目模板在App上下文中定义,不清楚如何将其附加到my-list组件上下文。我已经创建了处理模板及其变量的包装器指令,指令被包装到div中以捕获事件。它可以像这样使用:
@Directive({
selector: '[ngWrapper]'
})
export class NgWrapper
{
@Input()
private item:any;
private _viewContainer:ViewContainerRef;
constructor(_viewContainer:ViewContainerRef)
{
this._viewContainer = _viewContainer;
}
@Input()
public set ngWrapper(templateRef:TemplateRef)
{
var embeddedViewRef = this._viewContainer.createEmbeddedView(templateRef);
embeddedViewRef.setLocal('item', this.item)
}
}
@Component({
selector: 'my-list',
directives: [NgWrapper],
template: `
<template ngFor #item [ngForOf]="items">
<div (click)="onItemClicked(item)">
<template [ngWrapper]="userItemTemplate" [item]="item"></template>
</div>
</template>
`
})
class MyList {
@Input() items: string[];
@ContentChild(TemplateRef) userItemTemplate: TemplateRef;
userItemTemplate1: TemplateRef;
onItemClicked(item) {
console.log('Item click:', item);
}
ngAfterViewInit(){
this.userItemTemplate;
}
}
@Component({
selector: 'my-app',
directives: [MyList],
template: `
<my-list [items]='items'>
<template #item="item">
<h1>item: {{item}}</h1>
</template>
</my-list>
`
})
export class App {
items = ['this','is','a','test']
onItemClicked(item) {
console.log('Item click:', item);
}
}
解决方案不是完美的,但几乎是好的,请检查plunkr。