带有用户单击所选组件的动态选项卡

我试图设置一个选项卡系统,允许组件注册自己(与标题)。第一个选项卡就像一个收件箱,有大量的操作/链接项目可供用户选择,每一次点击都应该能够实例化一个新组件。动作/链接来自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. 如何在收件箱中创建一个动态列表来创建新的(不同的)标签?我有点猜测DynamicComponentBuilder将被使用?
  2. 如何从收件箱(单击)创建组件,并将其注册为选项卡并显示?我猜ng-content,但我找不到太多关于如何使用它的信息

编辑:试图澄清。

把收件箱想象成一个邮件收件箱。项目是作为JSON获取的,它显示了几个项目。一旦其中一个项目被单击,一个新的选项卡将被创建,该项目的动作为“类型”。然后,该类型就是一个组件。

编辑2: 图像

171677 次浏览

更新< / >强

Angular 5 StackBlitz示例 . blitz示例

更新

ngComponentOutlet被添加到4.0.0-beta.3

更新

有一个正在进行的NgComponentOutlet工作,它做一些类似https://github.com/angular/angular/pull/11235的事情

RC.7

< a href = " http://plnkr.co/edit/UGzoPTCHlXKWrn4p8gd1?p=preview" rel="noreferrer">活塞示例RC.7 . p=preview" rel="noreferrer">

// 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 {}

另见角。io动态组件装载机

旧版本 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

这在Angular2 RC.5中再次改变

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

砰砰作响的例子函数演示了如何在RC.5中动态创建组件

更新-使用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();
}
}
}
< p > 活塞示例RC.4 .4 < br > 活塞示例beta.17 . 0 < / p >

更新-使用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();
}
}
}

< a href = " https://plnkr.co/edit/kc2Bgg?p=preview" rel="noreferrer">柱塞示例beta.17 . p=preview" rel="noreferrer">柱塞示例beta.17 . p=preview

原始

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

Tabs组件获取传递的类型数组,并为数组中的每个项创建“选项卡”。

@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];
}

活塞示例beta.15 .1(不是基于你的Plunker)

还有一种方法可以将数据传递给动态创建的组件,比如(someData需要像type那样传递)

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

还支持对共享服务使用依赖项注入。

更多细节见https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html

我还没酷到可以发表评论。我固定的活塞从接受的答案为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'
},

< a href = " https://plnkr.co/edit/kVJvI1vkzrLZJeRFsZuv?p =预览noreferrer“rel = > https://plnkr.co/edit/kVJvI1vkzrLZJeRFsZuv?p =预览< / >

有组件准备使用(rc5兼容) ng2-steps 它使用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);
});
}


}