Skip to content

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

Closed

Description

@khaled-ansary

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

Activity

  1. 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[/+] on Sep 15, 2017
  2. filipesilva commented on Sep 22, 2017

    @filipesilva
    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).

  3. angular-automatic-lock-bot commented on Sep 7, 2019

    @angular-automatic-lock-bot

    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.

  4. locked and limited conversation to collaborators on Sep 7, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions