我正在try 设置一个标签系统,允许组件注册自己(带有标题).第一个选项卡就像一个收件箱,有很多操作/链接项供用户 Select ,每次单击都应该能够在单击时实例化一个新组件.这些操作/链接来自JSON.

然后,实例化的组件会将自身注册为新选项卡.

我不确定这是不是最好的方法?到目前为止,我看到的唯一指南是静态标签,这没有帮助.

到目前为止,我只得到了tabs服务,它在主应用程序中被 bootstrap 到整个应用程序中.它看起来像这样:

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个,但我找不到多少关于如何使用它的信息

EDIT:.试图澄清.

把收件箱想象成邮件收件箱.条目作为JSON获取,并显示多个条目.单击其中一个项目后,将使用该项目操作"type"创建一个新选项卡.然后,该类型就是一个组件.

EDIT 2: Image.

推荐答案

update个个

Angular 5 StackBlitz example

update个个

ngComponentOutlet添加到4.0.0-beta中.3.

update个个

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

RC.7

Plunker example 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 DYNAMIC COMPONENT LOADER

older versions xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

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

我将更新下面的示例,但这是假期前的最后一天.

Plunker example演示了如何在RC.5中动态创建组件

Update - use 100.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 example RC.4
Plunker example beta.17

Update - use 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 example beta.17

original

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

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

Plunker example beta.15(不是基于你的打劫)

还有一种传递数据的方法,可以将其传递给动态创建的组件,如(需要传递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

Angular相关问答推荐

使用Angular 17在 Select 选项中设置默认值

Angular 信号不更新afterNextender()

Swiper 11角17 SSR Swiper断点不工作

截获火灾前调用公共接口

所有返回缺少权限或权限不足的FireBase项目.

如果我只在组件中使用某个主题,是否需要取消订阅该主题?

Angular-每2个流式传输多个HTTP调用

使用 primeng Apollo 主题进行实时部署时多次加载 theme.css 和 preloading.css 文件

如何在 Angular14 中创建带有验证的自定义输入组件?

使用 Observable 进行渐进式填充

根据变量值更改按钮样式

如何在 Angular 2 模板中使用枚举

如何在Angular 4中为数字管道指定语言环境千位分隔符

如何在 Angular 2 中正确设置 Http 请求标头

AOT - Angular 6 - 指令 SomeComponent,预期 0 个参数,但go 有 1 个参数

在Angular 6 中找不到 HammerJS

为什么用?模板绑定中的运算符?

Angular 2 可用的 yeoman 生成器

如何在项目中导入 Angular material?

Angular2 Routerlink:添加查询参数