在 Angular 2 上使用 *ngFor 在多行中显示来自 @ContentChildren 的组件内容
Display component's content from @ContentChildren in multiple rows using *ngFor on Angular 2
我正在尝试使用 Angular 2 编写一个简单的列表视图(网格视图?),我可以通过这种方式实例化它:
<file-list [items]="items">
<file-list-column title="Id" field="id"></file-list-column>
<file-list-column title="Name" field="name"></file-list-column>
</file-list>
我实际上设法以一种非常 hacky 的方式完成了这项工作,它使用我在 FileListColumn 组件中设置的信息数据生成了一个 table。
这是我目前拥有的:
FileListColumn 组件,其中定义了列元数据:
import {Component, Input, TemplateRef, ViewChild} from '@angular/core';
@Component({
selector: 'file-list-column',
template: ''
})
export class FileListColumn {
@Input()
public title: string;
@Input()
public field: string;
ngOnInit() {
}
}
FileList组件,这是列表视图的主要组件:
import {Component, Input, ContentChildren, QueryList, AfterContentInit, forwardRef} from '@angular/core';
import {FileListColumn} from './file.list.column';
import {IItem} from 'models';
@Component({
selector: 'file-list',
template: require('./file.list.html')
})
export class FileList implements AfterContentInit {
@ContentChildren(forwardRef(() => FileListColumn))
public columns: QueryList<FileListColumn>;
@Input()
public items: IItem[];
constructor() {
}
public ngAfterContentInit() {
}
public getProperty(item:{[key:string]:any}, name:string) {
return item[name];
}
}
这 这是 FileList
组件的视图:
<table>
<thead>
<tr>
<th *ngFor="let col of columns">
{{col.title}}
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of items">
<td *ngFor="let col of columns">
{{getProperty(item, col.field)}}
</td>
</tr>
</tbody>
</table>
我提到的 hacky 事情是,它的工作方式,对于每个项目的每一列,它唯一做的就是调用传递项目和字段名称的 getProperty 函数和 returns该字段的值,这是一个不太理想的解决方案。
我现在想要做的是按照下面所示的方式在我的专栏中设置内容,并在我的 table:
中显示该内容
<file-list [items]="items">
<file-list-column title="Id" field="id"></file-list-column>
<file-list-column title="Name">
<span>{{$item.name}}</span>
</file-list-column>
<file-list-column title="Some other column">
<some-component item="$item"></some-component>
</file-list-column>
</file-list>
不仅如此,我还希望能够在 FileListColumn
组件本身中为列定义 html 以使代码更有条理。
我尝试的其中一件事是在 FileListColumn
组件的视图中添加一个模板:
<template #columnTemplate>
<span>text to see if worked</span>
<ng-content></ng-content>
</template>
然后我尝试在 FileListColumn
组件中获取它的引用:
@ViewChild('columnTemplate') columnTemplate: TemplateRef<any>;
然后我尝试使用以下方法将其加载到 FileList
组件的视图中:
<td *ngFor="let col of columns" *ngForTemplate="col.columnTemplate">
</td>
但我在浏览器上收到一条错误消息:
Error: Template parse errors:(…) "Error: Template parse errors:
Can't have multiple template bindings on one element. Use only one attribute named 'template' or prefixed with * ("
<tbody>
<tr *ngFor="let item of items">
<td *ngFor="let col of columns" [ERROR ->]*ngForTemplate="col.columnTemplate">
</td>
</tr>
"): FileList@10:44
即使这可行,我也不知道如何将我的项目数组中的项目实际绑定到 FileListColumn
内容。
有人知道我如何才能实现这些目标吗?
我整天都在寻找解决这个问题的方法,但我找不到任何有用的东西,大多数教程都是无关紧要的,因为它们是为使用旧 API 的 Angular 2 的旧测试版编写的。
在此先感谢您的帮助,抱歉这么长 post。
更新 1:
因此,我根据 Günter Zöchbauer 的链接之一尝试使用 #columnTemplate 尝试从 FileListColumn
加载模板 <td>
in FileList
:
<tr *ngFor="let item of items">
<td *ngFor="let col of columns">
<template [ngTemplateOutlet]="col.columnTemplate" [ngOutletContext]="item"></template>
</td>
</tr>
模板已加载到每个单元格中,但仍然存在两个问题:
- 我可以从模板访问的唯一属性是列属性,我无法从外部访问项目属性
ngFor
。
- 当我通过在列模板的
<ng-content>
中加载 <file-list-column><div>Content here</div></file-list-column>
将内容添加到 FileListColumn
时,此内容仅显示在 最后 行,意味着它不会在所有行中重复。
您可以这样使用ngForTemplate
:
<template ngFor let-col [ngForOf]="colums" [ngForTemplate]="col?.columnTemplate">
<td></td>
</template>
不过我不知道这是否能解决您所有的问题。
这可能也行(我自己没试过)
<td *ngFor="let col of columns template:col?.columnTemplate">
</td>
实际上我试过了 :-/ Repeating use of ng-content。也许有些事情已经改变,打破了这种方法。
另见
因此,我根据 Günter Zöchbauer 的回答中的链接找到了问题的解决方案。
在 FileList
组件中,我添加了一个 <template>
元素以从我的 FileListColumn
组件加载模板。现在看起来像这样:
<tr *ngFor="let item of items">
<td *ngFor="let col of columns">
<template [ngTemplateOutlet]="col.columnTemplate" [ngOutletContext]="{ item: item, column: col }"></template>
</td>
</tr>
如您所见,我将 item
作为 ngOutletContext
传递,因此我可以在模板中访问它。
现在,FileListColumn
的模板如下所示:
<template #internalColumnTemplate let-item="item">
<span *ngIf="field">{{item[field]}}</span>
<template *ngIf="externalTemplate" [ngTemplateOutlet]="externalTemplate" [ngOutletContext]="{ item: item }"></template>
</template>
它具有我可以在代码中引用的 #internalColumnTemplate
选择器和引用我在 ngOutletContext
中传递的项目并使其在模板内部可用的 let-item="item"
。
所以基本上,如果设置了 field
属性,它会显示带有字段值的 span
否则,如果设置了 externalTemplate
,这是我设置的另一个 属性 FileListColumn
组件,它显示我在 FileListColumn
.
的定义中传递的任何模板
这是完整的解决方案:
FileList组件file.list.ts:
import {Component, Input, ContentChildren, QueryList, AfterContentInit, forwardRef} from '@angular/core';
import {FileListColumn} from './file.list.column';
import {IItem} from 'models';
@Component({
selector: 'file-list',
template: require('./file.list.html')
})
export class FileList implements AfterContentInit {
@ContentChildren(forwardRef(() => FileListColumn))
public columns: QueryList<FileListColumn>;
@Input()
public items: IItem[];
constructor() {
}
public ngAfterContentInit() {
}
}
文件列表模板file.list.html:
<table>
<thead>
<tr>
<th *ngFor="let col of columns">
{{col.title}}
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of items">
<td *ngFor="let col of columns">
<template [ngTemplateOutlet]="col.columnTemplate" [ngOutletContext]="{ item: item }"></template>
</td>
</tr>
</tbody>
</table>
FileListColumn 组件 file.list.component.ts:
import {Component, Input, TemplateRef, ViewChild, ContentChild} from '@angular/core';
@Component({
selector: 'file-list-column',
template: require('./file.list.column.html')
})
export class FileListColumn {
@Input()
public title: string;
@Input()
public field: string;
@ContentChild(TemplateRef)
public externalTemplate: TemplateRef<any>;
@ViewChild('internalColumnTemplate')
public columnTemplate: TemplateRef<any>;
ngOnInit() {
}
}
externalTemplate
属性 是您在使用 FileList
组件时设置的可选模板,而 columnTemplate
是 FileListColumn
如前所述。
FileListColumn 模板file.list.column.html:
<template #internalColumnTemplate let-item="item">
<span *ngIf="field">{{item[field]}}</span>
<template *ngIf="externalTemplate" [ngTemplateOutlet]="externalTemplate" [ngOutletContext]="{ item: item }"></template>
</template>
以下是控件 FileList 组件在设置后的使用方式:
<div>
<file-list [items]="fakeItems">
<file-list-column title="Id">
<template let-item="item">
<div>external {{item.id}}{{item.name}}</div>
</template>
</file-list-column>
<file-list-column title="Name" field="name"></file-list-column>
</file-list>
</div>
如您所见,您可以选择只传递要使用的字段,列将解析它,或者您可以为列设置一个 <template>
html您想使用,并且不要忘记添加 let-item="item"
,如上所示,以便您可以访问该项目。
基本上就是这样。我希望这对其他人有用。
如果您有任何问题或改进建议,请告诉我,我会及时更新。
顺便说一下,我正在使用 Angular 2.0.1.
我正在尝试使用 Angular 2 编写一个简单的列表视图(网格视图?),我可以通过这种方式实例化它:
<file-list [items]="items">
<file-list-column title="Id" field="id"></file-list-column>
<file-list-column title="Name" field="name"></file-list-column>
</file-list>
我实际上设法以一种非常 hacky 的方式完成了这项工作,它使用我在 FileListColumn 组件中设置的信息数据生成了一个 table。
这是我目前拥有的:
FileListColumn 组件,其中定义了列元数据:
import {Component, Input, TemplateRef, ViewChild} from '@angular/core';
@Component({
selector: 'file-list-column',
template: ''
})
export class FileListColumn {
@Input()
public title: string;
@Input()
public field: string;
ngOnInit() {
}
}
FileList组件,这是列表视图的主要组件:
import {Component, Input, ContentChildren, QueryList, AfterContentInit, forwardRef} from '@angular/core';
import {FileListColumn} from './file.list.column';
import {IItem} from 'models';
@Component({
selector: 'file-list',
template: require('./file.list.html')
})
export class FileList implements AfterContentInit {
@ContentChildren(forwardRef(() => FileListColumn))
public columns: QueryList<FileListColumn>;
@Input()
public items: IItem[];
constructor() {
}
public ngAfterContentInit() {
}
public getProperty(item:{[key:string]:any}, name:string) {
return item[name];
}
}
这 这是 FileList
组件的视图:
<table>
<thead>
<tr>
<th *ngFor="let col of columns">
{{col.title}}
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of items">
<td *ngFor="let col of columns">
{{getProperty(item, col.field)}}
</td>
</tr>
</tbody>
</table>
我提到的 hacky 事情是,它的工作方式,对于每个项目的每一列,它唯一做的就是调用传递项目和字段名称的 getProperty 函数和 returns该字段的值,这是一个不太理想的解决方案。
我现在想要做的是按照下面所示的方式在我的专栏中设置内容,并在我的 table:
中显示该内容<file-list [items]="items">
<file-list-column title="Id" field="id"></file-list-column>
<file-list-column title="Name">
<span>{{$item.name}}</span>
</file-list-column>
<file-list-column title="Some other column">
<some-component item="$item"></some-component>
</file-list-column>
</file-list>
不仅如此,我还希望能够在 FileListColumn
组件本身中为列定义 html 以使代码更有条理。
我尝试的其中一件事是在 FileListColumn
组件的视图中添加一个模板:
<template #columnTemplate>
<span>text to see if worked</span>
<ng-content></ng-content>
</template>
然后我尝试在 FileListColumn
组件中获取它的引用:
@ViewChild('columnTemplate') columnTemplate: TemplateRef<any>;
然后我尝试使用以下方法将其加载到 FileList
组件的视图中:
<td *ngFor="let col of columns" *ngForTemplate="col.columnTemplate">
</td>
但我在浏览器上收到一条错误消息:
Error: Template parse errors:(…) "Error: Template parse errors:
Can't have multiple template bindings on one element. Use only one attribute named 'template' or prefixed with * ("
<tbody>
<tr *ngFor="let item of items">
<td *ngFor="let col of columns" [ERROR ->]*ngForTemplate="col.columnTemplate">
</td>
</tr>
"): FileList@10:44
即使这可行,我也不知道如何将我的项目数组中的项目实际绑定到 FileListColumn
内容。
有人知道我如何才能实现这些目标吗?
我整天都在寻找解决这个问题的方法,但我找不到任何有用的东西,大多数教程都是无关紧要的,因为它们是为使用旧 API 的 Angular 2 的旧测试版编写的。
在此先感谢您的帮助,抱歉这么长 post。
更新 1:
因此,我根据 Günter Zöchbauer 的链接之一尝试使用 #columnTemplate 尝试从 FileListColumn
加载模板 <td>
in FileList
:
<tr *ngFor="let item of items">
<td *ngFor="let col of columns">
<template [ngTemplateOutlet]="col.columnTemplate" [ngOutletContext]="item"></template>
</td>
</tr>
模板已加载到每个单元格中,但仍然存在两个问题:
- 我可以从模板访问的唯一属性是列属性,我无法从外部访问项目属性
ngFor
。 - 当我通过在列模板的
<ng-content>
中加载<file-list-column><div>Content here</div></file-list-column>
将内容添加到FileListColumn
时,此内容仅显示在 最后 行,意味着它不会在所有行中重复。
您可以这样使用ngForTemplate
:
<template ngFor let-col [ngForOf]="colums" [ngForTemplate]="col?.columnTemplate">
<td></td>
</template>
不过我不知道这是否能解决您所有的问题。
这可能也行(我自己没试过)
<td *ngFor="let col of columns template:col?.columnTemplate">
</td>
实际上我试过了 :-/ Repeating use of ng-content。也许有些事情已经改变,打破了这种方法。
另见
因此,我根据 Günter Zöchbauer 的回答中的链接找到了问题的解决方案。
在 FileList
组件中,我添加了一个 <template>
元素以从我的 FileListColumn
组件加载模板。现在看起来像这样:
<tr *ngFor="let item of items">
<td *ngFor="let col of columns">
<template [ngTemplateOutlet]="col.columnTemplate" [ngOutletContext]="{ item: item, column: col }"></template>
</td>
</tr>
如您所见,我将 item
作为 ngOutletContext
传递,因此我可以在模板中访问它。
现在,FileListColumn
的模板如下所示:
<template #internalColumnTemplate let-item="item">
<span *ngIf="field">{{item[field]}}</span>
<template *ngIf="externalTemplate" [ngTemplateOutlet]="externalTemplate" [ngOutletContext]="{ item: item }"></template>
</template>
它具有我可以在代码中引用的 #internalColumnTemplate
选择器和引用我在 ngOutletContext
中传递的项目并使其在模板内部可用的 let-item="item"
。
所以基本上,如果设置了 field
属性,它会显示带有字段值的 span
否则,如果设置了 externalTemplate
,这是我设置的另一个 属性 FileListColumn
组件,它显示我在 FileListColumn
.
这是完整的解决方案:
FileList组件file.list.ts:
import {Component, Input, ContentChildren, QueryList, AfterContentInit, forwardRef} from '@angular/core';
import {FileListColumn} from './file.list.column';
import {IItem} from 'models';
@Component({
selector: 'file-list',
template: require('./file.list.html')
})
export class FileList implements AfterContentInit {
@ContentChildren(forwardRef(() => FileListColumn))
public columns: QueryList<FileListColumn>;
@Input()
public items: IItem[];
constructor() {
}
public ngAfterContentInit() {
}
}
文件列表模板file.list.html:
<table>
<thead>
<tr>
<th *ngFor="let col of columns">
{{col.title}}
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of items">
<td *ngFor="let col of columns">
<template [ngTemplateOutlet]="col.columnTemplate" [ngOutletContext]="{ item: item }"></template>
</td>
</tr>
</tbody>
</table>
FileListColumn 组件 file.list.component.ts:
import {Component, Input, TemplateRef, ViewChild, ContentChild} from '@angular/core';
@Component({
selector: 'file-list-column',
template: require('./file.list.column.html')
})
export class FileListColumn {
@Input()
public title: string;
@Input()
public field: string;
@ContentChild(TemplateRef)
public externalTemplate: TemplateRef<any>;
@ViewChild('internalColumnTemplate')
public columnTemplate: TemplateRef<any>;
ngOnInit() {
}
}
externalTemplate
属性 是您在使用 FileList
组件时设置的可选模板,而 columnTemplate
是 FileListColumn
如前所述。
FileListColumn 模板file.list.column.html:
<template #internalColumnTemplate let-item="item">
<span *ngIf="field">{{item[field]}}</span>
<template *ngIf="externalTemplate" [ngTemplateOutlet]="externalTemplate" [ngOutletContext]="{ item: item }"></template>
</template>
以下是控件 FileList 组件在设置后的使用方式:
<div>
<file-list [items]="fakeItems">
<file-list-column title="Id">
<template let-item="item">
<div>external {{item.id}}{{item.name}}</div>
</template>
</file-list-column>
<file-list-column title="Name" field="name"></file-list-column>
</file-list>
</div>
如您所见,您可以选择只传递要使用的字段,列将解析它,或者您可以为列设置一个 <template>
html您想使用,并且不要忘记添加 let-item="item"
,如上所示,以便您可以访问该项目。
基本上就是这样。我希望这对其他人有用。
如果您有任何问题或改进建议,请告诉我,我会及时更新。
顺便说一下,我正在使用 Angular 2.0.1.