Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic import on Angular 7+

Tags:

angular

I'm trying to build project that contains some components with dynamic imports, like:

import {Directive, Input, ElementRef} from '@angular/core';

@Directive({
  selector: '[saKnob]'
})
export class KnobDirective {

  @Input() saKnob: any;
  constructor(private el: ElementRef) {
    import('jquery-knob').then(()=>{
      this.render()
    })
  }

  render(){
    $(this.el.nativeElement).knob(this.saKnob || {})
  }
}

The dynamic import on the constructor seems to be the problem. I'm getting the following error:

ERROR in ./src/app/shared/forms/input/knob.directive.ts 15:8
Module parse failed: 'import' and 'export' may only appear at the top level 
(15:8)
You may need an appropriate loader to handle this file type.
|         var _this = this;
|         this.el = el;
>         import('jquery-knob').then(function () {
|             _this.render();
|         });

As far as I researched, this kind of import is supported since Angular 4, and I'm using Angular 7.

Does anyone have an idea on what could be the problem?

* UPDATE *

As pointed by some answers, I was already using esnext on my tsconfig.app.file file:

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "outDir": "../out-tsc/app",
    "baseUrl": "./",
    "module": "esnext",
    "types": [],
    "paths": {
      "@app/*": ["app/*"],
      "@env/*": ["environments/*"]
    }
  },
  "exclude": [
    "src/test.ts",
    "**/*.spec.ts"
  ]
}

And here's the contents of the tsconfig.json

{
"compileOnSave": false,
"compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "module": "es2015",
    "target": "es5",
    "typeRoots": ["node_modules/@types"],
    "lib": ["es2018", "dom"],
    "paths": {
    "@app/*": ["src/app/*"],
    "@env/*": ["src/environments/*"]
    }
}
}

My typescript version is ~3.1.6.

like image 644
rbasniak Avatar asked Feb 27 '19 21:02

rbasniak


People also ask

Does angular support dynamic imports?

TypeScript 2.4 added support for dynamic import() expressions, which allow us to asynchronously load and execute ECMAScript modules on demand. This means that we can conditionally and lazily import other modules and libraries.

How do I import dynamic modules?

To load dynamically a module call import(path) as a function with an argument indicating the specifier (aka path) to a module. const module = await import(path) returns a promise that resolves to an object containing the components of the imported module.

What is the use of import and export in angular?

An import is what you put in the imports property of the @NgModule decorator. It enables an Angular module to use functionality that was defined in another Angular module. An export what you put is the exports property of the @NgModule decorator.


2 Answers

In short:

It's caused by npm issue appeared with @angular-devkit/build-angular update from 0.12.x to 0.13.x and related webpack update under the hood from 4.28.x to 4.29.x.

Possible solutions (workarounds):

  • use @angular-devkit/build-angular 0.12.x
  • try workarounds mentioned here. One working for me is to add explicit latest acorn dependency (e. g. npm i --save acorn will add 6.1.1 at the time of writing). Another popular workaround is to run npm update acorn --depth 20 && npm dedupe.
  • use yarn if applicable

Details:

Have recently stumbled upon similar issue after project update from Angular 7.2 to Angular 7.3. Before update build was fine, and esnext as target was already specified in tsconfig.json.

After some tests I've revealed that it relates to @angular-devkit/build-angular and found issues (13767, 13793) in angular-cli that surprisingly were closed. But alan-agius4 comment in issue 13793 shed some light to real origin: invalid hoisting for peer dependencies; explained in details by sokra in this comment.

There were already accepted pull request #147 for issue #4794, but then it was reverted in pull request #152 and issue #4794 remains opened at the time of writing.

dynamic-import

like image 162
Viachaslau T Avatar answered Oct 30 '22 08:10

Viachaslau T


You can't just call import like that. You need to import SystemJS first. then call its import method.

import('jquery-knob').then(()=>{
  this.render()
})

stackblitz: https://stackblitz.com/edit/angular-bkbqkj

like image 35
zmag Avatar answered Oct 30 '22 09:10

zmag