Search code examples
angularangular2-components

Angular2 Best way for dynamic component


I would like to know what is the best way (performance) to create dynamic component. I tried both but I'm not able to determine which one I should use.

With an ng-switch in my component.html container

@Component({
  selector: 'app-component-container',
  template: `<div [ngSwitch]="typeComponent">
              <app-component-one *ngSwitchCase="1" [value]="someValue"></app-component-one>
              <app-component-two *ngSwitchCase="2" [value]="someValue"></app-component-two>
              <app-component-three *ngSwitchCase="3" [value]="someValue"></app-component-three>
              </div>`
})
export class ContainerComponent implements OnInit {
  private typeComponent: number;
  private someValue: string;

  constructor() {
    this.typeComponent = 2;
    this.someValue = "Hello";

  }

  ngOnInit() {

  }
}

or with a component builder in my component.ts container

@Component({
  selector: 'app-component-container',
  template: '<div #container></div>'
})
export class ContainerComponent implements OnInit {
  @ViewChild('container', {read: ViewContainerRef}) container: ViewContainerRef;

  private typeComponent: number;

  private someValue: string;

  constructor(private _resolver: ComponentFactoryResolver) {
    this.typeComponent = 2;
    this.someValue = "Hello";
  }

  ngOnInit() {
    let childComponent: ComponentRef<any> = null;
    switch (this.typeComponent) {
      case 1:
        childComponent = this.container.createComponent<ChildComponentOne>(this._resolver.resolveComponentFactory(ChildComponentOne));
        break;
      case 2:
        childComponent = this.container.createComponent<ChildComponentTwo>(this._resolver.resolveComponentFactory(ChildComponentTwo));
        break;
      case 3:
        childComponent = this.container.createComponent<ChildComponentThree>(this._resolver.resolveComponentFactory(ChildComponentThree));
        break;
    }

    if (childComponent != null) {
      childComponent.instance.value = this.someValue;
    }
  }
}

This are simple exemple, in my application I have huge imbrication of dynamic component.

Thank you in advance for your answer guys.


Solution

  • Although both ways are viable, for the sake of DRYness, readability and future maintenance I would probably go the second way - that is, creating a dynamic component via the API and inserting it as a child of the container...

    You can further decrease duplication in your code this way:

    ngOnInit() {
      let childComponentType: Type = null;
      switch (this.typeComponent) {
        case 1:
          childComponentType = ChildComponentOne;
          break;
        case 2:
          childComponentType = ChildComponentTwo;
          break;
        case 3:
          childComponentType = ChildComponentThree;
          break;
      }
    
      if (childComponentType != null) {
        let factory = this._resolver.resolveComponentFactory(childComponentType);
        let instance: ComponentRef<any> = this.container.createComponent(factory);
    
        childComponent.instance.value = this.someValue;
      }
    }
    

    You could also have all of your 3 example components inherit a common base class and have your common attributes, methods and @Outputs in the base class. This way you could read values and subscribe to EventEmitters when each component share a common behaviour.

    Something along these lines:

    export class ChildComponentBaseClass {
      @Input() value;
    }
    
    @Component({...})
    export class ChildComponentOne<ChildComponentBaseClass> {
      ...
    }
    @Component({...})
    export class ChildComponentTwo<ChildComponentBaseClass> {
      ...
    }
    
    ngOnInit() {
      let childComponentType: Type = null;
      switch (this.typeComponent) {
        case 1:
          childComponentType = ChildComponentOne;
          break;
        case 2:
          childComponentType = ChildComponentTwo;
          break;
        case 3:
          childComponentType = ChildComponentThree;
          break;
      }
    
      if (childComponentType != null) {
        let factory = this._resolver.resolveComponentFactory(childComponentType);
        let instance: ComponentRef<ChildComponentBaseClass> = this.container.createComponent<ChildComponentBaseClass>(factory);
    
        // instance.value is now properly typed!
        childComponent.instance.value = this.someValue;
      }
    }