带有用户单击所选组件的动态选项卡 [英] Dynamic tabs with user-click chosen components
问题描述
我正在尝试建立一个选项卡系统,允许组件自行注册(带有标题).第一个选项卡就像一个收件箱,有很多操作/链接项可供用户选择,并且这些单击中的每一个都应能够在单击时实例化一个新组件.操作/链接来自JSON.
I'm trying to setup a tab system that allows for components to register themselves (with a title). The first tab is like an inbox, there's plenty of actions/link items to choose from for the users, and each of these clicks should be able to instantiate a new component, on click. The actions / links comes in from JSON.
实例化的组件随后将其自身注册为新选项卡.
The instantiated component will then register itself as a new tab.
我不确定这是否是最佳"方法?到目前为止,我所见的唯一指南仅针对静态标签,这无济于事.
I'm not sure if this is the 'best' approach? So far, the only guides I've seen are for static tabs, which doesn't help.
到目前为止,我只获得了tabs服务,该服务在main中自举,可以在整个应用程序中永久保存.看起来像这样:
So far, I've only got the tabs service which is bootstrapped in main to persist throughout the app. It looks something like this:
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);
}
}
问题:
- 如何在收件箱中创建一个动态列表,以创建新的(不同的)标签?我有点猜想
DynamicComponentBuilder
将被使用吗? - 如何通过收件箱(单击时)创建组件,将其自身注册为选项卡并显示?我正在猜测
ng-content
,但找不到有关如何使用它的很多信息
- How can I have a dynamic list in the inbox that creates new (different) tabs? I am sort of guessing the
DynamicComponentBuilder
would be used? - How can the components be created from the inbox (on click) register themselves as tabs and also be shown? I'm guessing
ng-content
, but I can't find much info on how to use it
编辑:尝试进行澄清.
将收件箱视为邮件收件箱.项目以JSON格式提取,并显示多个项目.单击其中一项后,将创建一个带有该项操作类型"的新标签.然后,该类型就是一个组件.
Think of the inbox as a mail inbox. Items are fetched as JSON and it displays several items. Once one of the items is clicked, a new tab is created with that items action 'type'. The type is then a component.
图片.
推荐答案
更新
更新
ngComponentOutlet
已添加到4.0.0-beta.3
ngComponentOutlet
was added to 4.0.0-beta.3
更新
正在进行的NgComponentOutlet
工作与 https://github.com类似/angular/angular/pull/11235
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.io动态组件加载器
较旧的版本 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
这在Angular2 RC.5中再次发生了变化
This changed again in Angular2 RC.5
我将更新以下示例,但这是休假前的最后一天.
I will update the example below but it's the last day before vacation.
此柱塞示例演示了如何在RC.5中动态创建组件
This Plunker example demonstrates how to dynamically create components in RC.5
更新-使用 ViewContainerRef .createComponent()
Update - use ViewContainerRef.createComponent()
由于不推荐使用DynamicComponentLoader
,因此该方法需要再次更新.
Because DynamicComponentLoader
is deprecated, the approach needs to be update again.
@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 example RC.4
Plunker example 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();
}
}
}
原始
从您的问题中不能完全确定您的要求是什么,但是我认为这应该可以满足您的要求.
Not entirely sure from your question what your requirements are but I think this should do what you want.
Tabs
组件获取一个传递类型的数组,并为该数组中的每个项目创建标签".
The Tabs
component gets an array of types passed and it creates "tabs" for each item in the array.
@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 example beta.15 (not based on your Plunker)
还有一种传递数据的方式,它可以传递给动态创建的组件,例如(someData
将需要传递给type
)
There is also a way to pass data along that can be passed to the dynamically created component like (someData
would need to be passed like type
)
this.dcl.loadIntoLocation(this.type, this.elRef, 'target').then((cmpRef) => {
cmpRef.instance.someProperty = someData;
this.cmpRef = cmpRef;
});
也有一些支持将依赖项注入与共享服务一起使用.
There is also some support to use dependency injection with shared services.
有关更多详细信息,请参见 https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html
For more details see https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html
这篇关于带有用户单击所选组件的动态选项卡的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!