Skip to content

import() feature doesn't work in production script to load modules or plugins dynamically #7703

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
khaled-ansary opened this issue Sep 15, 2017 · 2 comments

Comments

@khaled-ansary
Copy link

khaled-ansary commented Sep 15, 2017

Bug Report or Feature Request (mark with an x)

- [ ] bug report -> please search issues before submitting
- [ ] feature request

Versions.

$ ng -v
_ _ ____ _ ___
/ \ _ __ __ _ _ | | __ _ _ __ / | | | |
/ △ \ | '
\ / _ | | | | |/ _ | '
| | | | | | |
/ ___ | | | | (
| | || | | (| | | | || | | |
// __| ||_, |_,||_,|| _|||
|___/
@angular/cli: 1.4.1
node: 6.10.0
os: win32 x64
@angular/animations: 4.3.6
@angular/common: 4.3.6
@angular/compiler: 4.3.6
@angular/core: 4.3.6
@angular/forms: 4.3.6
@angular/http: 4.3.6
@angular/platform-browser: 4.3.6
@angular/platform-browser-dynamic: 4.3.6
@angular/router: 4.3.6
@angular/upgrade: 4.3.6
@angular/cli: 1.4.1
@angular/compiler-cli: 4.3.6
@angular/language-service: 4.3.6
typescript: 2.4.2

node --version
v6.10.0

Repro steps.

I am developing a plugin based app where plugins are loading dynamically. At the beginning, the plugins are unknown by the app i.e. the plugins are stored in a folder in the app. Now, I tried to load the plugin modules dynamically using import() method and compile to load the module and entryComponents using the following code.

export class DynamicComponent {    
        injector: Injector;
        compiler: Compiler;
        @ViewChild('container', {read: ViewContainerRef}) 
        container: ViewContainerRef; 
        componentRef: any; 

        constructor(private compFactoryResolver: ComponentFactoryResolver, injector: Injector,
            private apiService: apiService) {

            this.injector = ReflectiveInjector.resolveAndCreate(COMPILER_PROVIDERS, injector);
            this.compiler = this.injector.get(Compiler);
        }
        addWidget(){

          apiService.getMoudleUrls().subscribe( module_url=>{
            let module_= import(module_url); //e.g './data-widget/data-widget.module'    
            module_.then(module_data =>{

            const keys = Object.getOwnPropertyNames( module_data );
            let moduleFactories = this.compiler.compileModuleAndAllComponentsSync(module_data[keys[1]]);
            const moduleRef = moduleFactories.ngModuleFactory.create(this.injector);
            const componentFactory = moduleFactories.componentFactories
             .find(e => e.selector === 'data-widget'); // find the entry component using selector       
            this.componentRef = this.container.createComponent(componentFactory, null, moduleRef.injector);

            const newItem: WidgetComponent = this.componentRef.instance; 
       },
      error=>{});
     }
    }

Example module and components of the plugins are

data-widget.module.ts

@NgModule({
  imports: [ ...,
  declarations: [DataWidget],
  exports: [DataWidget],    
  providers:    [],
   entryComponents: [DataWidget]               
})
export class DataWidgetModule {
    constructor(private widgetService: widgetService) {
      widgetService.register('weather', DataWidget);   
    }        
 }
data-widget.component:

@Component({
   selector: 'data-widget',
   templateUrl: 'data-widget.component.html'
   })
   export class DataWidget{}

its working perfectly in developing mode with typescript. The problem start in production generated script. I build the project using ng build --prod and get Javascript bundles.
I tried two process to transpile typescript to javascript

  1. plugins library in javascript format using https://github.com/jvandemo/generator-angular2-library. and
  2. build plugins using tsc command

Then I tried to add the plugins by providing the URLs of the index.js (generated by no 1. process) and also tried to give URL of data-widget.module.js (generated by no 2. process). But I am failed to load the plugins and getting an error like ERROR Error: Uncaught (in promise): Error: No NgModule metadata found for 'undefined'.

The log given by the failure.

ERROR Error: Uncaught (in promise): Error: No NgModule metadata found for 'undefined'.
Error: No NgModule metadata found for 'undefined'.
at t.resolve (vendor.6269afbd95f1ed5b8299.bundle.js:1)
at t.getNgModuleMetadata (vendor.6269afbd95f1ed5b8299.bundle.js:1)
at t._loadModules (vendor.6269afbd95f1ed5b8299.bundle.js:1)
at t._compileModuleAndAllComponents (vendor.6269afbd95f1ed5b8299.bundle.js:1)
at t.compileModuleAndAllComponentsSync (vendor.6269afbd95f1ed5b8299.bundle.js:1)
at main.f86f90a36f2a8d2bd8a0.bundle.js:1
at t.invoke (polyfills.d1f049ba48d63f223a98.bundle.js:1)
at Object.onInvoke (vendor.6269afbd95f1ed5b8299.bundle.js:1)
at t.invoke (polyfills.d1f049ba48d63f223a98.bundle.js:1)
at r.run (polyfills.d1f049ba48d63f223a98.bundle.js:1)
at t.resolve (vendor.6269afbd95f1ed5b8299.bundle.js:1)
at t.getNgModuleMetadata (vendor.6269afbd95f1ed5b8299.bundle.js:1)
at t._loadModules (vendor.6269afbd95f1ed5b8299.bundle.js:1)
at t._compileModuleAndAllComponents (vendor.6269afbd95f1ed5b8299.bundle.js:1)
at t.compileModuleAndAllComponentsSync (vendor.6269afbd95f1ed5b8299.bundle.js:1)
at main.f86f90a36f2a8d2bd8a0.bundle.js:1
at t.invoke (polyfills.d1f049ba48d63f223a98.bundle.js:1)
at Object.onInvoke (vendor.6269afbd95f1ed5b8299.bundle.js:1)
at t.invoke (polyfills.d1f049ba48d63f223a98.bundle.js:1)
at r.run (polyfills.d1f049ba48d63f223a98.bundle.js:1)
at c (polyfills.d1f049ba48d63f223a98.bundle.js:1)
at c (polyfills.d1f049ba48d63f223a98.bundle.js:1)
at polyfills.d1f049ba48d63f223a98.bundle.js:1
at t.invokeTask (polyfills.d1f049ba48d63f223a98.bundle.js:1)
at Object.onInvokeTask (vendor.6269afbd95f1ed5b8299.bundle.js:1)
at t.invokeTask (polyfills.d1f049ba48d63f223a98.bundle.js:1)
at r.runTask (polyfills.d1f049ba48d63f223a98.bundle.js:1)
at o (polyfills.d1f049ba48d63f223a98.bundle.js:1)
at

Desired functionality.

Would like to load modules or plugins dynamically in production generated script as it loaded in developing mode.

Mention any other details that might be useful.

To make import() feature work, the following changes are necessary.

  1. typescript version 2.4
  2. in tsconfig.json file, change module: esnext
@khaled-ansary khaled-ansary changed the title import() feature doesn't work in production script import() feature doesn't work in production script to load modules or plugins dynamically Sep 15, 2017
@filipesilva
Copy link
Contributor

Heya, this is a usecase we don't have support for yet. The issue where it's being tracked is #4541. There are some workarounds there as well that may work in your scenario. If you see mentioned System.import() in them, it's the same as import() as used in your case (the difference is that the former doesn't need changes in tsconfig to work).

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 7, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants