Angular 2 ng For

时间:2016-11-13 20:22:28

标签: javascript angular

我知道有很多类似的问题,几乎所有问题都以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,..)

4 个答案:

答案 0 :(得分:6)

这是我的解决方案:

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>

答案 1 :(得分:4)

我写了另一个组件,比如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>

或者&#34;丑陋的ifs&#34; (这样你甚至可以摆脱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>

答案 2 :(得分:3)

我猜你可以使用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

答案 3 :(得分:2)

我的第一个想法是创建一个指令并使用Renderer class有条件地添加适当的组件。

<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的项加载模块。