2

I'm trying to created nested dynamic components based on json response.

public content={type:'paragraph',depth:1,text:'Root',entityRanges:[{type:'LINK',offset:83,length:16,data:{target:'_self',url:'/index.htm'}}],embbeded:[{type:'text',text:'This is Text component'}]}

So in the above structure, it as type paragraph so the ParagraphComponent need to render first.

It as an array object embbeded, in this array I wanted to render TextComponent.

So the final output should be like this,

<paraComp><p><textComp>This is Text component</textComp></p></paraComp>

Below is what I tried,

Main Component

export class AppComponent implements OnInit {
  @ViewChild('container', { read: ViewContainerRef })
  public target: ViewContainerRef;
  public content = { type: 'paragraph', depth: 1, text: 'Root', entityRanges: [{ type: 'LINK', offset: 83, length: 16, data: { target: '_self', url: '/index.htm' } }], embbeded: [{ type: 'text', text: 'This is Text component' }] }

  constructor(private createDynamicComponentService: CreateDynamicComponentService) { }
  ngOnInit() {
    this.createDynamicComponentService.createComponent(this.content, this.target);
  }
}

Main HTML

<ng-container #container></ng-container>

createDynamicComponentService

export class CreateDynamicComponentService {
  private rootViewContainer: ViewContainerRef;
  private componentFactory: ComponentFactory<any>;
  private componentReference;
  constructor(
    private componentFactoryResolver: ComponentFactoryResolver,
    @Inject(CONTENT_MAPPINGS) private contentMappings: any
  ) { }

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

  createComponent(content: any, container: ViewContainerRef) {
    const type = this.contentMappings[content.type];
    console.log(type);
    this.componentFactory = this.componentFactoryResolver.resolveComponentFactory(type);
    this.componentReference = this.rootViewContainer.createComponent(this.componentFactory);
    this.componentReference.instance.contentOnCreate(content);
  }
}

Link to Project https://stackblitz.com/edit/angular-dynamic-new?file=src/app/app.component.ts

I took reference from this https://github.com/sparkles-dev/ng-content-driven-angular/blob/master/src/app/reactive-content/content-host/content-host.component.ts

Based on it only I've created my project. But not able to make it work.

Please help.

1 Answer 1

2

The simplest way of doing this might look like:

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

  createComponent(content: any, container: ViewContainerRef) {
    const type = this.contentMappings[content.type];

    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(type);
    const componentRef = container.createComponent<any>(componentFactory);

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

    if (!content.embedded) return;

    // render children recursively
    for (const embeddedContent of content.embedded) {
      // in order to render children component must define ViewContainerRef
      if (!componentRef.instance.embeddedContainer) {
        const cmpName = componentRef.instance.constructor.name;
        throw new TypeError(`Trying to render embedded content. 
          ${cmpName} must have @ViewChild() embeddedContainer defined`);
      }

      this.createComponent(embeddedContent, componentRef.instance.embeddedContainer);
    }
  }

}

In order to render child components component should define container where the children will be rendered, for example:

paragraph.component.html

<p>
  <ng-container #embeddedContainer></ng-container>
</p>

paragraph.component.ts

export class ParagraphComponent implements OnInit {
  @ViewChild('embeddedContainer', { read: ViewContainerRef }) embeddedContainer: ViewContainerRef;

Forked Stackblitz

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.