簡體   English   中英

在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>

實際上,我設法以一種非常笨拙的方式使其工作,它使用我在FileListColumn組件中設置的信息數據生成了一個表。

這是我目前所擁有的:

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>

我提到的問題是,對於每個項目的每一列,它的工作方式都是,它調用getProperty函數,該函數傳遞項目和字段名稱並返回該字段的值,即不太理想的解決方案。

我現在想做的就是以如下所示的方式在我的列中設置內容,並在我的表中顯示該內容:

<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

即使這樣做有效,我也對如何將我的items數組中的項目實際綁定到FileListColumn內容FileListColumn

有人對我如何實現這些目標有想法嗎?

我整天都在研究解決此問題的方法,但是我找不到任何有用的東西,大多數教程都是無關緊要的,因為它們是使用較舊的API為Angular 2的舊beta編寫的。

在此先感謝您的幫助,對於冗長的帖子,我們深表歉意。

更新1:

所以,我設法加載從模板FileListColumn內的<td>中的FileList利用這樣的基礎上岡特Zöchbauer的環節之一#columnTemplate嘗試:

<tr *ngFor="let item of items">
    <td *ngFor="let col of columns">
        <template [ngTemplateOutlet]="col.columnTemplate" [ngOutletContext]="item"></template>
    </td>
</tr>

模板加載到每個單元格中,但是仍然存在兩個問題

  • 我只能從模板訪問的屬性是列屬性,而不能從外部ngFor訪問項目屬性。
  • 當我通過執行<file-list-column><div>Content here</div></file-list-column>並將其添加到FileListColumn的內容以加載到列模板的<ng-content>中時,此內容僅在最后一行顯示,這意味着不會在所有行中都重復顯示。

因此,我根據GünterZö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選擇器,以及具有let-item="item"引用,該引用引用了我在ngOutletContext傳遞的項目,並使其在模板內可用。 因此,基本上,如果設置了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() {
    }
}

FileList模板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組件時設置的可選模板,而columnTemplateFileListColumn視圖中的內部模板,如上和以下所示。

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

您可以通過以下方式使用ngForTemplate

<template ngFor let-col [ngForOf]="colums" [ngForTemplate]="col?.columnTemplate">
  <td></td>
</template>

我不知道這是否可以解決您所有的問題。

這可能也很好(我自己沒有嘗試過)

<td *ngFor="let col of columns template:col?.columnTemplate">
</td>

實際上,我嘗試過:-/ 重復使用ng-content 也許某些改變改變了這種方法。

也可以看看

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM