3

I Want to know how to create nested dynamic components and maintains its parent child relationship.

For example, I have data like this,

- A
--A.1
--A.2
-B
--B.1
-C 

I wanted to create the component like this,

<A>
   <A1></A1>
   <A2></A2>
</A>
<B>
   <B1></B1>
</B>
<C></C>

But with my code I could only create parent component or child component. But not both.

Below is my code,

  setRootViewContainerRef(view: ViewContainerRef): void {
    this.rootViewContainer = view;
  }

  createComponent(content: any, type: any) {
 console.log(content);
    if (content.child && content.child.length > 0) {
      content.child.forEach(type => {
        const typeP = this.contentMappings[type.type];
        this.createComponent(type, typeP);
      });
    } else {
      this.renderComp(content,type)
    }
  }

  renderComp(content,type) {
    if (!type) {
      return
    }
    this.componentFactory = this.componentFactoryResolver.resolveComponentFactory(type);
    this.componentReference = this.rootViewContainer.createComponent(this.componentFactory);

    if (this.componentReference.instance.contentOnCreate) {
      this.componentReference.instance.contentOnCreate(content);
    }
  }

With this code, I get this output.

Link to working example, StackBlitz

Please help me to resolve this issue.


Updated.

Even after adding the viewChild, It still throws the viewchild not defined.

Refer this image, In the component.instance I'm not seeing the view child element.

enter image description here

Updated stackblitz link https://stackblitz.com/edit/angular-dynamic-new-mepwch?file=src/app/content/a/a.component.ts

user007
  • 189
  • 2
  • 13

1 Answers1

5

You should create ViewContainer on each level that is going to render child components:

a.component.html

<p>
a works!
</p>
<ng-container #container></ng-container>

a.component.ts

export class AComponent implements OnInit {
  @ViewChild('container', { read: ViewContainerRef, static: true }) embeddedContainer: ViewContainerRef;

And then render component to dedicated container:

create-dynamic-component.service.ts

@Injectable()
export class CreateDynamicComponentService {
  constructor(
    private componentFactoryResolver: ComponentFactoryResolver,
    @Inject(CONTENT_MAPPINGS) private contentMappings: any,
    private inlineService: InlineService
  ) { }


  createComponent(content: any, type: any, vcRef) {
    const componentRef = this.renderComp(content, type, vcRef)
    if (content.child && content.child.length) {
      if (!componentRef.instance.embeddedContainer) {
        const cmpName = componentRef.instance.constructor.name;
        throw new TypeError(`Trying to render embedded content. ${cmpName} must have @ViewChild() embeddedContainer defined`);
      }

       content.child.forEach(type => {
        const typeP = this.contentMappings[type.type];
        this.createComponent(type, typeP, componentRef.instance.embeddedContainer);
      });
    }
  }

  renderComp(content,type, vcRef: ViewContainerRef) {
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(type);
    const componentRef = vcRef.createComponent<any>(componentFactory);

    if (componentRef.instance.contentOnCreate) {
      componentRef.instance.contentOnCreate(content);
    }

    return componentRef;
  }
}

Note how renderComp method takes ViewContainerRef from the component with children:

 this.createComponent(type, typeP, componentRef.instance.embeddedContainer);

Forked Stackblitz

yurzui
  • 205,937
  • 32
  • 433
  • 399
  • It's not working in Angular v8. I get this error `Property 'instance' does not exist on type 'void'.ts(2339)` – user007 Jul 25 '19 at 20:00
  • Looks like you forgot something. Try this version https://stackblitz.com/edit/angular-dynamic-new-tbz6kx?file=src/app/create-dynamic-component.service.ts – yurzui Jul 25 '19 at 20:05
  • As you said I've added the viewChild in A comp also updated the html, But in console it shows that `must have @ViewChild() embeddedContainer defined`. – user007 Jul 25 '19 at 20:44
  • Have you tried my example? Did you see I defined `` in AComponent? If it gives you that error then it means that component doesn't have container defined – yurzui Jul 25 '19 at 20:54
  • Yes I've defined. For for some reason `emmbededComponent` is not coming in instance. I've added like this in A comp ` @ViewChild('container', { read: ViewContainerRef, static: false }) embeddedContainer: ViewContainerRef;` and in html `` – user007 Jul 25 '19 at 21:01
  • Please reproduce it in stackblitz – yurzui Jul 25 '19 at 21:05
  • I replicated the issue, Please check this https://stackblitz.com/edit/angular-dynamic-new-mepwch?file=src/app/content/a/a.component.ts – user007 Jul 25 '19 at 21:05
  • 1
    `@ViewChild('container', { read: ViewContainerRef, static: true })` https://stackblitz.com/edit/angular-dynamic-new-j7v3za?file=src/app/content/a/a.component.ts – yurzui Jul 25 '19 at 21:06
  • Yes. Worked Thanks. You saved my day – user007 Jul 25 '19 at 21:10
  • @yurzui Great answer. You should update it with what's in your comment: the "static: true" part. – BigJ Oct 31 '20 at 23:42