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:图像
更新
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' },