Angular 2dynamic标签,用户点击select的组件

我试图设置一个允许组件注册自己(标题)的选项卡系统。 第一个选项卡就像一个收件箱,有大量的动作/链接项目供用户select,每个点击都应该能够实例化一个新的组件,点击。 操作/链接来自JSON。

实例化后的组件将自己注册为一个新的选项卡。

我不确定这是否是最好的方法? 沙发我见过的唯一指南是静态选项卡,这并没有帮助。

到目前为止,我只有在主引导的标签服务持续整个应用程序,看起来像这样的东西。

export interface ITab { title: string; } @Injectable() export class TabsService { private tabs = new Set<ITab>(); addTab(title: string): ITab { let tab: ITab = { title }; this.tabs.add(tab); return tab; } removeTab(tab: ITab) { this.tabs.delete(tab); } } 

问题:

1)如何在收件箱中创build一个dynamic列表来创build新的(不同的)标签? 我有点猜测DynamicComponentBuilder将被使用?

2)如何从收件箱创build的组件(点击)注册自己的标签,也被显示? 我猜ng-content,但我找不到有关如何使用它的很多信息

编辑:尝试澄清

将收件箱视为邮件收件箱,将项目提取为JSON并显示多个项目。 一旦其中一个项目被点击,一个新的选项卡与该项目的行动“types”创build。 该types是一个组件

编辑2:图像

View post on imgur.com

更新

ngComponentOutlet被添加到4.0.0-beta.3

更新

有一个NgComponentOutlet工作正在进行,类似于https://github.com/angular/angular/pull/11235

RC.7

Plunker例子RC.7

 // Helper component to add dynamic components @Component({ selector: 'dcl-wrapper', template: `<div #target></div>` }) export class DclWrapper { @ViewChild('target', {read: ViewContainerRef}) target: ViewContainerRef; @Input() type: Type<Component>; cmpRef: ComponentRef<Component>; private isViewInitialized:boolean = false; constructor(private componentFactoryResolver: ComponentFactoryResolver, private compiler: Compiler) {} updateComponent() { if(!this.isViewInitialized) { return; } if(this.cmpRef) { // when the `type` input changes we destroy a previously // created component before creating the new one this.cmpRef.destroy(); } let factory = this.componentFactoryResolver.resolveComponentFactory(this.type); this.cmpRef = this.target.createComponent(factory) // to access the created instance use // this.compRef.instance.someProperty = 'someValue'; // this.compRef.instance.someOutput.subscribe(val => doSomething()); } ngOnChanges() { this.updateComponent(); } ngAfterViewInit() { this.isViewInitialized = true; this.updateComponent(); } ngOnDestroy() { if(this.cmpRef) { this.cmpRef.destroy(); } } } 

用法示例

 // Use dcl-wrapper component @Component({ selector: 'my-tabs', template: ` <h2>Tabs</h2> <div *ngFor="let tab of tabs"> <dcl-wrapper [type]="tab"></dcl-wrapper> </div> ` }) export class Tabs { @Input() tabs; } 
 @Component({ selector: 'my-app', template: ` <h2>Hello {{name}}</h2> <my-tabs [tabs]="types"></my-tabs> ` }) export class App { // The list of components to create tabs from types = [C3, C1, C2, C3, C3, C1, C1]; } 
 @NgModule({ imports: [ BrowserModule ], declarations: [ App, DclWrapper, Tabs, C1, C2, C3], entryComponents: [C1, C2, C3], bootstrap: [ App ] }) export class AppModule {} 

另请参阅angular.iodynamic分量装载机

旧版本 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

这在Angular2 RC.5中再次发生了变化

我会更新下面的例子,但是这是休假前的最后一天。

这个Plunker示例演示了如何在RC.5中dynamic创build组件

更新 – 使用ViewContainerRef .createComponent()

由于DynamicComponentLoader已弃用,因此需要再次更新该方法。

 @Component({ selector: 'dcl-wrapper', template: `<div #target></div>` }) export class DclWrapper { @ViewChild('target', {read: ViewContainerRef}) target; @Input() type; cmpRef:ComponentRef; private isViewInitialized:boolean = false; constructor(private resolver: ComponentResolver) {} updateComponent() { if(!this.isViewInitialized) { return; } if(this.cmpRef) { this.cmpRef.destroy(); } this.resolver.resolveComponent(this.type).then((factory:ComponentFactory<any>) => { this.cmpRef = this.target.createComponent(factory) // to access the created instance use // this.compRef.instance.someProperty = 'someValue'; // this.compRef.instance.someOutput.subscribe(val => doSomething()); }); } ngOnChanges() { this.updateComponent(); } ngAfterViewInit() { this.isViewInitialized = true; this.updateComponent(); } ngOnDestroy() { if(this.cmpRef) { this.cmpRef.destroy(); } } } 

Plunker例子RC.4
Plunker示例beta.17

更新 – 使用loadNextToLocation

 export class DclWrapper { @ViewChild('target', {read: ViewContainerRef}) target; @Input() type; cmpRef:ComponentRef; private isViewInitialized:boolean = false; constructor(private dcl:DynamicComponentLoader) {} updateComponent() { // should be executed every time `type` changes but not before `ngAfterViewInit()` was called // to have `target` initialized if(!this.isViewInitialized) { return; } if(this.cmpRef) { this.cmpRef.destroy(); } this.dcl.loadNextToLocation(this.type, this.target).then((cmpRef) => { this.cmpRef = cmpRef; }); } ngOnChanges() { this.updateComponent(); } ngAfterViewInit() { this.isViewInitialized = true; this.updateComponent(); } ngOnDestroy() { if(this.cmpRef) { this.cmpRef.destroy(); } } } 

Plunker示例beta.17

原版的

从你的问题不完全确定你的要求是什么,但我认为这应该做你想要的。

Tabs组件获取传递types的数组,并为数组中的每个项目创build“选项卡”。

 @Component({ selector: 'dcl-wrapper', template: `<div #target></div>` }) export class DclWrapper { constructor(private elRef:ElementRef, private dcl:DynamicComponentLoader) {} @Input() type; ngOnChanges() { if(this.cmpRef) { this.cmpRef.dispose(); } this.dcl.loadIntoLocation(this.type, this.elRef, 'target').then((cmpRef) => { this.cmpRef = cmpRef; }); } } @Component({ selector: 'c1', template: `<h2>c1</h2>` }) export class C1 { } @Component({ selector: 'c2', template: `<h2>c2</h2>` }) export class C2 { } @Component({ selector: 'c3', template: `<h2>c3</h2>` }) export class C3 { } @Component({ selector: 'my-tabs', directives: [DclWrapper], template: ` <h2>Tabs</h2> <div *ngFor="let tab of tabs"> <dcl-wrapper [type]="tab"></dcl-wrapper> </div> ` }) export class Tabs { @Input() tabs; } @Component({ selector: 'my-app', directives: [Tabs] template: ` <h2>Hello {{name}}</h2> <my-tabs [tabs]="types"></my-tabs> ` }) export class App { types = [C3, C1, C2, C3, C3, C1, C1]; } 

Plunker示例beta.15 (不基于你的Plunker)

还有一种方法可以将数据传递给dynamic创build的组件,例如( someData需要像type一样传递)

  this.dcl.loadIntoLocation(this.type, this.elRef, 'target').then((cmpRef) => { cmpRef.instance.someProperty = someData; this.cmpRef = cmpRef; }); 

还有一些支持使用dependency injection与共享服务。

有关更多详情,请参阅https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html

有组件准备使用(rc5兼容) ng2步骤使用Compiler注入组件到步骤容器和服务为所有连接(数据同步)

  import { Directive , Input, OnInit, Compiler , ViewContainerRef } from '@angular/core'; import { StepsService } from './ng2-steps'; @Directive({ selector:'[ng2-step]' }) export class StepDirective implements OnInit{ @Input('content') content:any; @Input('index') index:string; public instance; constructor( private compiler:Compiler, private viewContainerRef:ViewContainerRef, private sds:StepsService ){} ngOnInit(){ //Magic! this.compiler.compileComponentAsync(this.content).then((cmpFactory)=>{ const injector = this.viewContainerRef.injector; this.viewContainerRef.createComponent(cmpFactory, 0, injector); }); } } 

我不够酷的评论。 我从被接受的答案中解决了这个问题,为rc2工作。 没有什么奇特的,CDN的链接只是被打破了。

 '@angular/core': { main: 'bundles/core.umd.js', defaultExtension: 'js' }, '@angular/compiler': { main: 'bundles/compiler.umd.js', defaultExtension: 'js' }, '@angular/common': { main: 'bundles/common.umd.js', defaultExtension: 'js' }, '@angular/platform-browser-dynamic': { main: 'bundles/platform-browser-dynamic.umd.js', defaultExtension: 'js' }, '@angular/platform-browser': { main: 'bundles/platform-browser.umd.js', defaultExtension: 'js' }, 

https://plnkr.co/edit/kVJvI1vkzrLZJeRFsZuv?p=preview