我知道有很多类似的问题,几乎所有问题都以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>
当然不行。
<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删除操作)。
<div *ngFor="let item of items">
<dynamic-component-loader [item]="item"></dynamic-component-loader>
</div>
目前我还不记得DynamicComponentLoader
究竟是如何运作的(我在很久以前就已经在angular2 alpha的类似问题中尝试过了)。但是我记得代码对我来说就像hack
..对于这样的常见任务?..
我不知道自己做错了什么,也许问题是我仍然在Angular 1
进行思考?使用它我会使用ngInclude
或自定义指令和模板函数。
伙计们,您有其他解决方案吗?不要坚持我的两个可能的解决方案,也许我需要开箱即用,并在我的应用程序的不同部分完全解决这个问题。我很困惑。谢谢:)
让我们说你的任务是用Angular 2编写Facebook。我认为你会在试图显示新闻提要时遇到同样的问题。每个新闻Feed都有其类型(text
,event
,ads
,..)
答案 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的项加载模块。