24
app
 |-plugins
       |-plugin1
           |-config.json
           |-plugin1.module.ts
           |-plugin1.component.ts

       |-plugin2
           |-config.json
           |-plugin2.module.ts
           |-plugin2.component.ts

As you can see above, I have "app/plugins" folder, which contains plugins. Each plugin will contain one "config.json" file which will tell some configuration including -

{
   path: "feature1",
   moduleFile: "feature1.module",
   moduleClassName: "Feature1Module"
}

So what I want is, before application bootstrap it will scan the "app/plugins" folder and load all plugin configurations, and lazily register all module routes. For above example the route will be

{
     path: "feature1",
     loadChildren: "app/plugins/plugin1/plugin1.module#Plugin1Module"
}

That way, we can drop new plugin into plugin folder and refresh the application, and our newly dropped plugin is up and running.

Anyone knows how can I achieve this?

NOTE: I am on angular2 latest(2.1.0)

4
  • Why not just use NgModule for each feature? Commented Oct 14, 2016 at 10:01
  • 1
    I am using NgModule for each feature, but I want to know how can I read all folders and read configuration so that I can register those modules route lazily. Commented Oct 14, 2016 at 10:25
  • I guess you'll have to write a nodejs script and generate code manually. It's not Angular thing... Commented Oct 14, 2016 at 10:34
  • 2
    Did you get this working? I'm looking at doing something similar. Commented Mar 15, 2017 at 15:10

1 Answer 1

15

I'm looking for the same behavior than the one you're describing and I think I've found how to do it, thanks to this github issue : Lazy loading components without Route

Here is the code I've written to do it : plunker here

  • First : dynamic.module.ts, the dynamically loaded module and its component

    import { Component, NgModule } from '@angular/core'
    
    @Component({
        selector: 'my-test',
        template: `<h1>html template of TestComponent from DynamicModule</h1>`
    })
    
    export class TestComponent { }
    
    @NgModule({
        declarations: [TestComponent],
        exports: [TestComponent]
    })
    
    export class DynamicModule { }
    
  • Second : here is the component which dynamically loads module when you give it the module path.

    import {
    Component, 
    ViewContainerRef,
    Compiler,
    ComponentFactory,
    ComponentFactoryResolver,
    ModuleWithComponentFactories,
    ComponentRef,
    ReflectiveInjector,
    SystemJsNgModuleLoader } from '@angular/core';
    
    class ModuleNode {
        modulePath: string;
        componentName: string;
    }
    
    @Component({
        moduleId: module.id,
        selector: 'widgetContainer',
        templateUrl: './widgetContainer.component.html'
    })
    
    export class WidgetContainerComponent {
        widgetConfig: string;
        module: ModuleNode;
        cmpRef: ComponentRef<any>;
    
    constructor(private widgetService: WidgetLoader,
        private viewref: ViewContainerRef,
        private resolver: ComponentFactoryResolver,
        private loader: SystemJsNgModuleLoader,
        private compiler: Compiler){}
    
    openWebApp(menu:any) {
        this.loader.load(menu.modulePath)  // load the module and its components
            .then((modFac) => {
                // the missing step, need to use Compiler to resolve the module's embedded components
                this.compiler.compileModuleAndAllComponentsAsync<any>(modFac.moduleType)
    
                    .then((factory: ModuleWithComponentFactories<any>) => {
                        return factory.componentFactories.find(x => x.componentType.name === menu.componentName);
                    })
                    .then(cmpFactory => {
    
                        // need to instantiate the Module so we can use it as the provider for the new component
                        let modRef = modFac.create(this.viewref.parentInjector);
                        this.cmpRef = this.viewref.createComponent(cmpFactory, 0, modRef.injector);
                        // done, now Module and main Component are known to NG2
    
                    });
            });
    }
    
    ngOnDestroy() {
        if (this.cmpRef) {
            this.cmpRef.destroy();
        }
    }
    

    }

What do you think about that? Does it help? Thanks a lot for your feedback.

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

4 Comments

Is this working if your Angular app is deployed with AOT ?
@DouaBeri : No. Unfortunatly this does not work with AOT. We had to give this away because of AOT compilation.
i wish i had asked this question and would have accepted it as a genious answer , brilliant bro , js brilliant , bundle of thanks
Brillian , can we have a chat for 5 mins please , skype-- wikiwaleed2

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.