簡體   English   中英

Angular 2 ngFor中不同組件的列表

[英]List of different components in Angular 2 ngFor

我知道有很多類似的問題,幾乎所有問題都以DynamicComponentLoader答案結束,但我認為下面描述的用例是如此簡單和常見(IMO),Angular 2的解決方案應該是直截了當的。

示例用例

我有一系列新聞項目,其屬性type描述了它是什么類型的項目。

var items = [
  { id: 1, type: 'text', data: {} },
  { id: 2, type: 'text', data: {} },
  { id: 3, type: 'text-two-columns', data: {} },
  { id: 4, type: 'image-text', data: {} },
  { id: 5, type: 'image', data: {} },
  { id: 6, type: 'twitter', data: {} },
  { id: 7, type: 'text', data: {} }
]

每種不同的類型都有不同的view ,背后有完全不同的邏輯。 換句話說 - 每種type都有自己的angular2 Component

所以我試圖實現的抽象代碼是:

<div *ngFor="let item of items">
   <item-{{item.type}} [data]="item.data"></item-{{item.type}}>
</div>

當然它不會起作用。

可能的解決方案#1

<div *ngFor="let item of items">
   <item-text *ngIf="item.type === 'text'" [data]="item.data"></item-text>
   <item-image *ngIf="item.type === 'image'" [data]="item.data"></item-image>
   ...
</div>

我不喜歡這個解決方案不僅因為它看起來很丑陋而且我每次添加新類型時都必須包含這一行但是我想知道這個解決方案從性能角度來看是否合適? 我的意思是,如果我有10,000種不同的類型,只顯示3個項目。 因此angular2必須從DOM中移除9,999個標簽,並且每個3個項目只留下一個(3 * 9999刪除操作)。

可能的解決方案#2

<div *ngFor="let item of items">
   <dynamic-component-loader [item]="item"></dynamic-component-loader>
</div>

目前我不記得DynamicComponentLoader究竟是如何工作的(我已經在很久以前的angular2 alpha中嘗試了類似的問題)。 但是我記得代碼看起來像是我的hack ..對於這樣的常見任務?...

Angular 1.x思考

我不知道我做錯了什么,也許問題是我仍然認為在Angular 1 使用它我會使用ngInclude或自定義指令與模板功能。

伙計們,你有其他解決方案嗎? 不要堅持我的兩個可能的解決方案,也許我需要開箱即用,並在我的應用程序的不同部分完全解決這個問題。我很困惑。 謝謝:)

編輯:一個更真實的例子

假設您的任務是使用Angular 2編寫Facebook。我認為您在嘗試顯示新聞源時會遇到同樣的問題。 每個新聞Feed都有它的類型( texteventads ......)

這是我的解決方案:

import { Component, OnInit, ViewContainerRef, TemplateRef, ComponentFactoryResolver, Input } from '@angular/core';

@Component({
  selector: 'item',
  template: '',
  styleUrls: ['./item.component.scss']
})
export class ItemComponent implements OnInit {
  @Input() type: string;
  @Input() data: any;

  constructor(
    private viewContainerRef: ViewContainerRef,
    private componentFactoryResolver: ComponentFactoryResolver,
    private componentLookupService: YourComponentLookUpService
  ) { }

  ngOnInit() {
    const component = this.componentLookupService.findByType(type);
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(component);
    // Look at the https://angular.io/docs/ts/latest/api/core/index/ViewContainerRef-class.html#!#createComponent-anchor for more information about how to use DI... in the createComponent function.
    const componentRef =this.viewContainerRef.createComponent(componentFactory);
    // Or you can then access the newly created component here: this.componentRef.instance
  }

}

在你的NgFor循環中:

<div *ngFor="let item of items">
   <item [type]="item.type" [data]="item.data"></item>
</div>

我會寫另一個組件,比如item-flex

<item-flex [item]="item" *ngFor="let item of items"></item-flex>

item-flex可以使用ngSwitch

<div [ngSwitch]="item.type">
    <item-text *ngSwitchCase="'text'" [data]="item.data"></item-text>
    <item-image *ngSwitchCase="'image'" [data]="item.data"></item-image>
    <span *ngSwitchDefault >UNKNOWN TYPE:{{item.type}}</span>
</div>

或者“丑陋的ifs”(這樣你甚至可以擺脫ngSwitch解決方案中存在的外部標簽/ div / span):

<item-text *ngIf="item.type=='text'" [data]="item.data"></item-text>
<item-image *ngIf="item.type=='image'" [data]="item.data"></item-image>

我想你可以使用Angular 4附帶的“ngComponentOutlet”,它根據傳遞的值動態創建組件。 我沒有測試過代碼。

@Component({
    selector: 'my-app',
    template: `
    <h1>Angular version 4</h1>
    <div *ngFor="let <component name> of <list of component name>">
        <ng-container *ngComponentOutlet="<component name>">enter code here</ng-container>
    </div>
  `,
})

請參閱網址了解更多詳情: https//netbasal.com/a-taste-from-angular-version-4-50be1c4f3550

我的第一個想法是創建一個指令並使用Renderer類有條件地添加適當的組件。

<div app-item [type]="item.type" [data]="item.data"></div>

指示

import { Directive, ElementRef, Input, Renderer,  OnInit } from '@angular/core';

@Directive({
  selector: '[app-item]'
})
export class ItemDirective implements OnInit {
    @Input('type') type: string;
    @Input('data') data: any[];
    constructor(private el: ElementRef, private r: Renderer) {  }

    ngOnInit(): void {
        switch(this.type){
        case: 'text'
            let self = this.r.createElement( this.el.nativeElement, 'item-text' );
            this.r.setElementAttribute(self, 'data', 'this.data')
            break;
        case: 'image');
            let self = this.r.createElement( this.el.nativeElement, 'item-image'
            this.r.setElementAttribute(self, 'data', 'this.data')

            break;
        // ... so on ...
    }
}

您可以使用更多@Inputs傳遞參數並使用其他Renderer方法附加它們。

這使視圖非常簡單,並且不會為不需要tyoe的項加載模塊。

暫無
暫無

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

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