Alexander Alexander - 1 month ago 53
TypeScript Question

Angular 2 components polymorphism

I would like to implement a component acting like a container with dynamically added children components. The type and number of children components should be configured on server side.

So Models arrived form server could look like this:

export class ModelBase {
public data: any;
}

export class ModelA extends ModelBase {
public dataA: any;
}

export class ModelB extends ModelBase {
public dataB: any
}


Simplified components could be like this:

@Component({
selector: "component-base"
})
export class BaseComponent {
@Input() model: ModelBase;
}

@Component({
selector: "component-a",
template: "<div>component-a</div>"
})
export class AComponent extends BaseComponent {
@Input() model: ModelBase;
}

@Component({
selector: "component-b",
template: "<div>component-b</div>"
})
export class BComponent extends BaseComponent {
@Input() model: ModelBase;
}


And here is the Application and how I would like to work with my components:

@Component({
selector: 'app',
template: `
<div *ngFor="#model of models">
<component-base [model]="model"></component-base>
</div>
`
})
export class App {
}


I want 'component-base' will be replaced with the concrete implementation based on 'model' type. For example with component-a.
Does workflow like this is possible to implement with Angular 2?

rnj rnj
Answer

There might be a better API for this functionality coming: https://github.com/angular/angular/pull/11235.

In the meantime, check out http://stackoverflow.com/a/36325468/5307109. You'll likely have to modify it so that you can pass data through it to your destination component and call a custom component hook on load.

Using the wrapper described above, your ComponentBase will have a template of its own and inject each model's associated component into the wrapper.

export class ModelA extends ModelBase {
  dataA: any;
  component: any = AComponent;
}

@Component({
  selector: "component-base",
  template: `
    <dcl-wrapper [type]="model.component" [init-data]="model"></dcl-wrapper>
  `
})
export class BaseComponent {
  @Input() model: ModelBase;
}

@Component({
  selector: "component-a",
  template: "<div>component-a</div>"
})
export class AComponent {
  @Input() model: ModelBase;

  /**
   * Custom hook you might create in DclWrapper.
   */
  onDclInit(model: ModelBase) : void {
    this.model = model;
  }

}