Search code examples
angularjsangularangular2-components

Angular RC 2 - Dynamically Render Components from a json file


DynamicComponentLoader has been deprecated. I am trying to build a parent component that renders child components inside of the parent component based on an array of child components, meaning that each user has a settings file that holds the child components that need to be in the parent. Right now the parent looks something like this:

import { Component, OnInit, Input, ComponentResolver, ViewContainerRef } from '@angular/core';

import { Child1Component } from '../children/child1.component'
import { Child2Component } from '../children/child2.component'
import { Child3Component } from '../children/child3.component'

import { ParentService } from './parent.service'

@Component({
  selector: 'parent',
  directives: [Child1Component, Child2Component, Child3Component],
  providers: [ParentService],
  template: `
    <style>
      .parent{
        background-image: linear-gradient(141deg, #8ecb45 0%, #97cd76 71%, #96d885 100%);
        width: 100%;
        height: 200px;
      }
    </style>
    <div class="parent"></div>
  `
})
export class ParentComponent {
  constructor(private parentService:ParentService, private children: any, viewContainer: ViewContainerRef, private componentResolver: ComponentResolver) {

    this.children = parentService.getChildren();

    for(var i = 0; i < children.length; i++) {
      this.componentResolver.resolveComponent(children[i].component)
        .then(componentFactory => {
          const ctxInjector = viewContainer.injector;
          return viewContainer.createComponent(componentFactory, 0, ctxInjector);
        })
    }

  }
}

Right now, call the service in the ParentComponent constructor seems to be causing some issues. But before I was working on looping over the componentResolver, I was able to attach a component underneath then parent node, but not within it.

Does anyone know a better way to build a dynamic parent component? This is to manage a dashboard type of a layout. Most examples are explicit about how many components will be loaded. This wont work for me. I have seen a few posts about how to make this work, but so far I have not seen anything running on with the RC with a config file.

This comes closest to what I am trying to do: http://plnkr.co/edit/jAmMZKz2VqponmFtPpes?p=preview but it uses the dcl...

Currently I am getting an error that I have not figured out when launching the app: TypeError: Cannot read property 'query' of null

Thanks for the help!

Here is a link if you feel like downloading it and giving it a stab:

https://github.com/weswhite/ng2-layout

EDIT/UPDATE: It may have to do with the children: any I am injecting. I dont think I am doing that correctly, still figuring that bit out...


Solution

  • You can use ComponentResolver with a child ViewContainerRef to create the component dynamically and load them in the parent component.

    @Component({
      selector: 'parent',
      providers: [ParentService],
      template: `
        <style>
          .parent{
            background-image: linear-gradient(141deg, #8ecb45 0%, #97cd76 71%, #96d885 100%);
            width: 100%;
            height: 200px;
          }
        </style>
        <div #content></div>
       `
      })
    
    export class ParentComponent {
      @ViewChild('content', { read: ViewContainerRef }) contentContainer: ViewContainerRef;
    
      children: Component[];
    
      constructor(
        private parentService:ParentService,
        private resolver: ComponentResolver) {
        this.children = parentService.getChildren();
      }
    
      ngOnInit() {
        this.children.forEach( (component, index) => this.loadComponent(component, index));
      }
    
      private loadComponent(component: Component, index: number) {
        return this
          .resolver
          .resolveComponent(component)
          .then( factory => 
            this.contentContainer.createComponent(factory, index, this.contentContainer.injector))
      }
    }
    

    I also upgraded the provided plunker to use latest @angular and the ComponentResolver instead of the DynamicComponentLoader: Plunker: http://plnkr.co/edit/Z6bXwBcwAnf4DSpNlU8H?p=preview

    Using DCL with loadNextTolocation: http://plnkr.co/edit/NYgJzz9UjrtGsNnO5WXS?p=info

    P.S: You were getting TypeError: Cannot read property 'query' of null, because of the way you were injecting children, you have to specify an injectable Type.

    See also Angular 2 dynamic tabs with user-click chosen components