带有用户单击所选组件的动态选项卡 [英] Dynamic tabs with user-click chosen components

查看:87
本文介绍了带有用户单击所选组件的动态选项卡的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试建立一个选项卡系统,允许组件自行注册(带有标题).第一个选项卡就像一个收件箱,有很多操作/链接项可供用户选择,并且这些单击中的每一个都应能够在单击时实例化一个新组件.操作/链接来自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);
    }
}

问题:

  1. 如何在收件箱中创建一个动态列表,以创建新的(不同的)标签?我有点猜想DynamicComponentBuilder将被使用吗?
  2. 如何通过收件箱(单击时)创建组件,将其自身注册为选项卡并显示?我正在猜测ng-content,但找不到有关如何使用它的很多信息
  1. 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?
  2. 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.

图片.

推荐答案

更新

> Angular 5 StackBlitz示例

更新

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

柱塞示例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();
    }    
  }
}

> 柱塞示例RC.4
柱塞示例beta.17

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();
    }    
  }
}

> Plunker示例beta.17

原始

从您的问题中不能完全确定您的要求是什么,但是我认为这应该可以满足您的要求.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆