使用Angular 2中的ComponentResolver创建动态内容的动态选项卡

时间:2016-06-23 13:18:12

标签: angular

受到Angular 2 dynamic tabs with user-click chosen componentsPassing Input while creating Angular 2 Component dynamically using ComponentResolver的启发我试图更进一步,不仅让标签获得动态组件,而且标签的内容也包含动态组件。 从原始示例中,我使用了组件C1-C3作为选项卡,并希望将C4和C5用于部分。我知道实际标签没有任何样式/功能,但结构应该足以让我继续前进。 I made this.

//our root app component
import {Component, ComponentRef, Input, ViewContainerRef, ComponentResolver, ViewChild, Injectable, OnInit} from '@angular/core';

@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.type).then((factory:ComponentFactory<any>) => {
      this.cmpRef = this.target.createComponent(factory);
      this.cmpRef.instance.info = this.type;
    });
  }

  ngOnChanges() {
    this.updateComponent();
  }

  ngAfterViewInit() {
    this.isViewInitialized = true;
    this.updateComponent();  
  }

  ngOnDestroy() {
    if(this.cmpRef) {
      this.cmpRef.destroy();
    }    
  }
}

@Component({
  selector: 'child-dcl-wrapper',
  template: `<div #target></div>`
})
export class ChildDclWrapper {
  @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.type).then((factory:ComponentFactory<any>) => {
      this.cmpRef = this.target.createComponent(factory);
      this.cmpRef.instance.info = this.type;
    });
  }

  ngOnChanges() {
    this.updateComponent();
  }

  ngAfterViewInit() {
    this.isViewInitialized = true;
    this.updateComponent();  
  }

  ngOnDestroy() {
    if(this.cmpRef) {
      this.cmpRef.destroy();
    }    
  }
}

@Component({
  selector: 'c1',
  template: `<h2>c1</h2><p>{{info.name}}</p>
            <my-sections [sections]="section"></my-sections>`

})
export class C1 {
}

@Component({
  selector: 'c2',
  template: `<h2>c2</h2><p>{{info.name}}</p>
            <my-sections [sections]="section"></my-sections>`
})
export class C2 {
}

@Component({
  selector: 'c3',
  template: `<h2>c3</h2><p>{{info.name}}</p>
            <my-sections [sections]="section"></my-sections>`

})
export class C3 {
}

@Component({
  selector: 'c4',
  template: `<h2>c4</h2><p>{{info.name}}</p>`

})
export class C4 {
}

@Component({
  selector: 'c5',
  template: `<h2>c5</h2><p>{{info.name}}</p>`

})
export class C5 {
}

@Component({
  selector: 'my-sections',
  directives: [ChildDclWrapper],
  template: `
  <h3>Sections</h3>
  <div *ngFor="let section of type.sections">
    <child-dcl-wrapper [type]="section"></child-dcl-wrapper>
  </div>
`
})
export class Sections {
  @Input() sections;
}

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

@Injectable()
export class AService {
  info = [
    {
      name: "taco",
      type: C1,
      sections: [
        {
          name: "believe",
          type: C4
        },
        {
          name: "car",
          type: C5
        }
      ]
    },
    {
      name: "pete",
      type: C2,
      sections: [
        {
          name: "repeat",
          type: C4
        },
        {
          name: "banana",
          type: C5
        }
      ]
    },
    {
      name: "carl",
      type: C3,
      sections: [
        {
          name: "shotgun",
          type: C4
        },
        {
          name: "helmet",
          type: C5
        }
      ]
    }
  ];

  getServiceInfo() {
    console.log("Bung.");
    return this.info;
  }
}

@Component({
  selector: 'my-app',
  directives: [Tabs],
  providers: [AService],
  template: `
  <h1>Hello {{name}}</h1>
  <my-tabs [tabs]="types"></my-tabs>
`
})
export class App extends OnInit {
  types;
  aService;

  constructor(private aService: AService) {
    this.aService = aService;
  }

  getInfo() {
    console.log("beep");
    this.types = this.aService.getServiceInfo();
  }

  ngOnInit() {
    console.log("boop");
    this.getInfo();
  }
}

1 个答案:

答案 0 :(得分:2)

我在您的代码中看到了几个错误。例如,您需要在以下代码中使用info.sections而不是section

<my-sections [sections]="section"></my-sections>

应该是:

<my-sections [sections]="info.sections"></my-sections>

此外,您忘记添加Sections指令,如下所示:

@Component({
  selector: 'c1',
  template: `<h2>c1</h2><p>{{info.name}}</p>
            <my-sections [sections]="info.sections"></my-sections>`,
  directives: [Sections] <== add this line

})
export class C1 {}

你可以使用一个DclWrapper来做到这一点。

更新的plunkr在这里https://plnkr.co/edit/JRyqou9yC6LvSRrCR3eA?p=preview