7

I am building a modal component in Angular 2 (and TypeScript) that will have different views/pages. It won't have tabs, but the concept is pretty similar. Basically I am struggling to find an approach to doing this.

Within my modal component, I want to output the different views/pages. Each one should be a component itself, because they don't have any markup in common. Plus, they will fetch data from service classes, so I need to be able to write specific logic for each view.

Basically what I want is something like:

// Within my modal component's template
<div *ngFor="#view of views">
    // Render view component here
</div>

Where views could be an array of the components, for instance. My first and main problem is that I am not sure how to output a collection of my views (components).

How can I output a collection of components within another component?

Also, I need a way to hide and show views, which is the only thing the views have in common. I was thinking of adding an isActive variable to the views and showing/hiding them based on that. Would it be bad practice for the views components to either implement a ModalView interface or extend from a base class to add this logic? From my modal component, I want to be able to control which view is displayed, so I need the views to have this logic in common.

I hope it is clear what I want to do. The solution may be simple, but right now I am a bit confused on how to go about it. A code example would be much appreciated!

SnareChops
  • 13,175
  • 9
  • 69
  • 91
ba0708
  • 10,180
  • 13
  • 67
  • 99

2 Answers2

5

You can create a "component factory" as a directive that loads appropriate components using DynamicComponentLoader, based on parameters you pass from your modal.

<component-factory *ngFor="#view of views" 
    (loaded)="addComponent($event)"
    [name]="view.name" 
    [visible]="view.visible"></component-factory>

@Directive({ selector: 'component-factory' })
class ComponentFactory {
  @Input() name;
  @Input() visible;
  @Output() loaded = new EventEmitter();
  constructor(
    private _dcl: DynamicComponentLoader, 
    private _eref: ElementRef) {

  // import OneCmp, TwoCmp in this file...
  private components: { one: OneCmp, two: TwoCmp }

  ngOnInit() {
    this._dcl.loadNextToLocation(
      this.components[this.name], 
      this._eref)
      // pass values to the loaded components
      // and send it's instance to the parent
      .then((ref: ComponentRef) => {
        ref.instance.visible = this.visible;
        this.loaded.emit(ref.instance)
      });
  }
}
Sasxa
  • 40,334
  • 16
  • 88
  • 102
  • Oh, that looks awesome, thanks a lot! I am thinking that in case I need to interact further with my views, I might want to pull the code out of the directive and embed that logic into the modal component (or at least reference the factory there), because then I can keep references to the loaded components. If you have any feedback on this approach, I would love to hear it. Nevertheless, now I have some very useful reference code, so thumbs up for that! – ba0708 Feb 22 '16 at 22:57
  • I was thinking that directive would be more reusable. You can send reference back to the parent component through `@Output()`, by emitting `(ref: ComponentRef)` and collecting them in the modal. – Sasxa Feb 23 '16 at 05:29
  • Super, that was the missing piece of the puzzle. Thank you very much - very helpful! – ba0708 Feb 24 '16 at 00:27
  • Any solution without the deprecated `DynamicComponentLoader` ? – cocoseis Jan 01 '17 at 22:33
  • @cocoseis this answer helped me a lot and seems to be kept quite up to date: http://stackoverflow.com/a/36325468/504075 – ccondrup Feb 21 '17 at 17:42
1

Angular2 Dynamically Render Template

import { Component, View, DynamicComponentLoader, ElementRef} from 'angular2/core'; import {bootstrap} from 'angular2/platform/browser'

@Component({
    selector: 'some-component',
    properties: ['greeting'],
    template: `
    <b>{{ greeting }}</b>
  `
})
class SomeComponent { }




@Component({
    selector: 'app'
})
@View({
    template: `
    <h1>Before container</h1>
    <div #container></div>
    <h2>After container</h2>
  `
})
class App {
    loader: DynamicComponentLoader;
    elementRef: ElementRef;

    constructor(loader: DynamicComponentLoader, elementRef: ElementRef) {
    this.laoder = loader;
    this.elementRef = elementRef;

    // Some async action (maybe ajax response with html in it)
    setTimeout(() => this.renderTemplate(`
      <div>
    <h2>Hello</h2>
    <some-component greeting="Oh, hey"></some-component>
      </div>

full code : https://github.com/guyoung/GyPractice-Angular2Advanced/tree/master/apps/dynamically_render_template

guyoung
  • 129
  • 1