WesW WesW - 5 months ago 558
AngularJS Question

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...

Answer

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