1

This is the sole guide I can find.

https://angular.io/guide/dynamic-component-loader

I am using the same method so everything is quite basic. Until you have a recursive component.

Let's imagine this shape of data:

{
   component: {
       id: "container",
       components: [
           {
              id: "container",
              components: [
                  {
                       id: "text",
                       data: "Hi"
                  }
              ]
           }
       ]
   }
}

What this means is that:

  1. Container requires access to DynamicComponentLoader
  2. DynamicComponentLoader can construct a Container
  3. See the first step.

Angular warns me that there is a Circular Dependency and quite rightly so.

But I have spent a week on this issue and I definitely cannot see a solution. I think I am going to have to stop using dynamic components, and am forced to return to using @Input. Like ButtonContainer and ImageContainer and so on. I can't seem to make a generic container. I am stuck on a flat surface.

That is a real pain. Does anyone have any ideas?

4
  • Are you talking about ComponentFactoryResolver ? Could you provide a sample ? Commented Jul 19, 2018 at 13:18
  • 1
    I created a working sample without any problems base on angular.io/guide/… stackblitz.com/edit/angular-dsivwn Commented Jul 19, 2018 at 16:20
  • @JEY I am sorry I took so long, I was mulling over your amazing sample! Thanks for taking the time to do that!! I ditched my "Service based" component creation and moved it into a ContainerComponent just like your sample. Now there is no circular reference and I am a happy man. Do you want to move this comment into an answer so I can accept it? Thanks again! Commented Jul 20, 2018 at 15:20
  • 1
    I add the answer thanks. Commented Jul 23, 2018 at 12:10

1 Answer 1

1

I post my comment as an answer as requested by @OP.

There is a great example in the official angular documentation on how to create dynamic component. Basically it contains a component (AdBannerComponent) that can create component from a given list using ComponentFactoryResolver.

Based on this example we can create a new component that just reuse the AdBannerComponent:

import { Component, Input, ComponentFactoryResolver, ViewChild, OnInit } from '@angular/core';
import { AdComponent } from './ad.component';
import { AdItem } from './ad-item';
import { AdDirective } from './ad.directive';
@Component({
  template: `
    <div class="job-ad">
    <app-ad-banner [ads]="ads"></app-ad-banner>
    </div>
`})
export class HeroSubProfileComponent implements AdComponent, OnInit {
  @Input() data: any;
  ads: AdItem[];
  constructor() { }
  ngOnInit() {
    this.ads = this.data;
  }
}

This component will create dynamically the component given as an input in data. We can also directly reuse AdBannerComponent as is by updating it's definition to match AdItem:

export class AdBannerComponent implements OnInit, OnDestroy {
  @Input() ads: AdItem[];
  currentAdIndex = -1;
  @ViewChild(AdDirective) adHost: AdDirective;
  interval: any;
  @Input() data: any;

  constructor(private componentFactoryResolver: ComponentFactoryResolver) { }

  ngOnInit() {
    if (this.data != null) {
      this.ads = this.data;
    }
    this.loadComponent();
    this.getAds();
  }

If we update the AdService (providing the list of component to create) with the following:

@Injectable()
export class AdService {
  getAds() {
    return [
      new AdItem(HeroProfileComponent, {name: 'Bombasto', bio: 'Brave as they come'}),

      new AdItem(HeroProfileComponent, {name: 'Dr IQ', bio: 'Smart as they come'}),

      new AdItem(HeroJobAdComponent,   {headline: 'Hiring for several positions',
                                        body: 'Submit your resume today!'}),

      new AdItem(HeroJobAdComponent,   {headline: 'Openings in all departments',
                                        body: 'Apply today'}),
      // component using composition
      new AdItem(HeroSubProfileComponent, [new AdItem(HeroProfileComponent, {name: 'Great Scott', bio: 'Awesome'})]),
      // directly using AdBannerComponent
      new AdItem(AdBannerComponent, [new AdItem(HeroProfileComponent, {name: 'Bombasto', bio: 'Brave as they come'})])
    ];
  }
}

We are creating dynamic component inside another one and we can do it as deep as we want.

You can find a running example here https://stackblitz.com/edit/angular-dsivwn.

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.