Search code examples
angulartypescriptsystemjs

Using SystemJS to import application modules in Angular 2


I'm working on an Angular 2 with Typescript project using SystemJS. Right now I find often we're having to do things like '../../../' to load components from other modules depending on how deeply nested we are in a given component. It would seem like using SystemJS's map setting would allow me to have application-wide modules available for import without that horrible relative pathing approach, but I can't determine definitively if I can do that. I'm also open to other ways to solve this.

Basically what I'd like to do is something like this:

import {Component} from '@angular/core';
import {SecureHttpClient} from 'mySecurityModule'; //No ../../../

@Component({
  moduleId: module.id,
  selector: 'my-selector',
  templateUrl: 'my.component.html'
})
export class MyComponent {

  constructor(private client: SecureHttpClient) {

  }
}

I tried the configuration below but it doesn't work (TS compilation fails).

(function (global) {
  System.config({
    paths: {
      // paths serve as alias
      'npm:': 'node_modules/'
    },
    // map tells the System loader where to look for things
    map: {
      // our app is within the app folder
      app: 'app',

      // angular bundles
      '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
      '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
      '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
      '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
      '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
      '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
      '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
      '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
      '@angular/upgrade': 'npm:@angular/upgrade/bundles/upgrade.umd.js',


      // other libraries
      'rxjs':                      'npm:rxjs',
      'angular-in-memory-web-api': 'npm:angular-in-memory-web-api',
      '@ng-bootstrap/ng-bootstrap': 'npm:@ng-bootstrap/ng-bootstrap/bundles/ng-bootstrap.js',

      // Internal Modules
      'mySecurityModule': 'app/secure/secure.module'

    },
    // packages tells the System loader how to load when no filename and/or no extension
    packages: {
      app: {
        main: './main.js',
        defaultExtension: 'js'
      },
      rxjs: {
        defaultExtension: 'js'
      },
      'angular-in-memory-web-api': {
        main: './index.js',
        defaultExtension: 'js'
      }
    }
  });
})(this);

I know there is likely other ways to do this, but again can't find anything definitive and it's a problem I'd really like to solve. Thanks again!


Solution

  • You can setup typescript compiler to use base URL and import from there. In tsconfig.json add:

      "compilerOptions": {
        "baseUrl": "./",
        ...
      }
    

    then you can use app root as an absolute path. Something like

    import {SecureHttpClient} from 'app/mySecurityModule';
    

    instead of:

    import {SecureHttpClient} from '../../../mySecurityModule';
    

    Actual paths might be different for you.

    However, I would recommend using some sort of Import extension for your editor (for example, AutoImport for VSCode), since some tools (AoT, CLI) might have an issue with this. Hopefully things will improve as tools get more mature (;