简体   繁体   中英

Dynamically load components without breaking AoT - Angular2

So I was trying to create a module which would take a bunch of components and load them dynamically but without messing with AoT. This has proven a challenge but I'm getting really close except for one thing. How do I retrieve all the components which are stored in the ComponentFactory?

In mij app.module.ts I pass an array of components to the forRoot() of my dynamic-load module:

imports: [
  DynamicLoadModule.forRoot(dynamicComponents),
]

In my dynamic-load.module.ts I add all of them to the Angular2 ComponentFactory:

static forRoot(components: any[]) {
        return {
            ngModule: DynamicLoadModule,
            providers: [{
                provide: ANALYZE_FOR_ENTRY_COMPONENTS,
                useValue: components,
                multi: true,
            }, {
                provide: DynamicLoadService,
                useValue: new DynamicLoadService(components)
            }]
        };
    }

Now the problem lies in the DynamicLoadService , which is messing with AoT. I've tried the documented export-a-function way but this doesn't solve the problem because this way I'm unable to pass the components as parameters to the service. So the following found in the Angular documentation is not providing a solution for my problem:

let heroServiceFactory = (logger: Logger, userService: UserService) => {
  return new HeroService(logger, userService.user.isAuthorized);
};

export let heroServiceProvider =
  { provide: HeroService,
    useFactory: heroServiceFactory,
    deps: [Logger, UserService]
  };

So then I tried passing the components to my dynamic-load.module by the use of OpaqueToken , which was also of no help because I then had uncompiled components at best and my module lost a lot of flexibilty.

So I came to the conclusion that, if I needed an array of all the components in the ComponentFactory , I just need a way to retrieve them all.
So, where I now use the ComponentFactoryResolver to get one template based on the name I need to get them all, or at least get all the names of the components in the factory.

if (selectedComponent !== '') {
  if (typeof this.currentComponent !== 'undefined') { this.currentComponent.destroy(); }

  const   components = this.dynamicLoadService.getComponents(), // --> instead of getting a list of available components through this service which needs the factory that blocks AoT, I should just be able to retrieve all available components from the ComponentFactory
          component: any = components.find((x) => x.name.toLowerCase().indexOf(selectedComponent) > -1),
          compFactory = this.cfr.resolveComponentFactory(component);

  this.currentComponent = this.vcr.createComponent(compFactory);
  this.currentComponent.instance.data = this.componentData;
}

Edit So the first answer did not quite solve the problem but another helpful person was able to help me fix the problem through the Angular Github .

In the end my code looks as follows:

import { NgModule, ANALYZE_FOR_ENTRY_COMPONENTS, Inject } from '@angular/core';
import { DynamicLoadService } from './dynamic-load.service';

import { DynamicLoadComponent } from './dynamic-load.component';

export function DynamicLoadFactory(cmps) {
    return new DynamicLoadService(cmps);
}

@NgModule({
    declarations: [
        DynamicLoadComponent
    ],
    exports: [
        DynamicLoadComponent
    ]
})
export class DynamicLoadModule {
    static forRoot(components: any[]) {
        return {
            ngModule: DynamicLoadModule,
            providers: [{
                provide: ANALYZE_FOR_ENTRY_COMPONENTS,
                useValue: components,
                multi: true
            },
            {
                provide: 'ENTRIES',
                useValue: components
            },
            {
                provide: DynamicLoadService,
                useFactory: DynamicLoadFactory,
                deps: [[new Inject('ENTRIES')]]
            }]
        };
    }
}

As was pointed out to me ANALYZE_FOR_ENTRY_COMPONENTS is not part of dependency injection tree (Find the source here ). So the above workaround deals with that problem.


So the answer was presented to me be a very helpful person at the Angular Github.

It was suggested to me that I should just

Just inject ANALYZE_FOR_ENTRY_COMPONENTS into DynamicLoadService

Which now looks like this:

export function DynamicLoadFactory(cmps) {
  return new DynamicLoadService(cmps);
}

export class DynamicLoadModule {
    static forRoot(components: any[]) {
        return {
            ngModule: DynamicLoadModule,
            providers: [{
                provide: ANALYZE_FOR_ENTRY_COMPONENTS,
                useValue: components,
                multi: true,
            }, {
                provide: DynamicLoadService,
                useFactory: DynamicLoadFactory,
                deps: [new Inject(ANALYZE_FOR_ENTRY_COMPONENTS)]
            }]
        };
    }
}

source at Github

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM