Repository navigation
import() feature doesn't work in production script to load modules or plugins dynamically #7703
Description
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
- plugins library in javascript format using https://github.com/jvandemo/generator-angular2-library. and
- build plugins using
tsccommand
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.
- typescript version 2.4
- in tsconfig.json file, change
module: esnext
Activity
[-]import() feature doesn't work in production script[/-][+]import() feature doesn't work in production script to load modules or plugins dynamically[/+]filipesilva commentedon Sep 22, 2017
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 asimport()as used in your case (the difference is that the former doesn't need changes in tsconfig to work).angular-automatic-lock-bot commentedon Sep 7, 2019
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.