Angular 2 bundle like import / Import multiple modules
Asked Answered
B

3

12

I didn't know how to state the question... So, basically I'm writing my own application using Angular 2 with Typescript. I would like to make it possible to import my modules the way we do with Angular, where we can import multiple related modules in one line. For example, we could do this in angular2 beta:

import { Component, OnInit, Input } from 'angular2/core';

I would like to do something similar with my app. For example:

import { UserService, RoleService } from 'my-project/services';

Also, I want to be able to do the same for models, pipes, components, etc...

One more thing, the folder structure would be something like this:

src/app/services
src/app/components
src/app/models
src/app/pipes

What I tried to do: On path src/app, I created on file for each 'bundle', like services.d.ts, models.d.ts, pipes.d.ts... And then I tried to map on SystemJS config, like so:

(function(global) {

  // map tells the System loader where to look for things
  var map = {
    'app':                        'src/app', // 'dist',
    'rxjs':                       'node_modules/rxjs',
      'my-project/components':                  'src/app/components',
      'my-project/services':                'src/app/services',
      'my-project/models':                        'src/app/models',
      'my-project/pipes':                           'src/app/pipes',
    '@angular':                   'node_modules/@angular'
  };

  // packages tells the System loader how to load when no filename and/or no extension
  var packages = {
    'app':                        { format: 'register', main: 'main.js',  defaultExtension: 'js' },
    'rxjs':                       { defaultExtension: 'js' },
  };

  var packageNames = [
    'my-project/components',
    'my-project/services',
    'my-project/models',
    'my-project/pipes',
    '@angular/common',
    '@angular/compiler',
    '@angular/core',
    '@angular/http',
    '@angular/platform-browser',
    '@angular/platform-browser-dynamic',
    '@angular/router-deprecated',
    '@angular/testing',
    '@angular/upgrade',
  ];

  // add package entries for angular packages in the form '@angular/common': { main: 'index.js', defaultExtension: 'js' }
  packageNames.forEach(function(pkgName) {
    packages[pkgName] = { main: 'index.js', defaultExtension: 'js' };
  });

  var config = {
    map: map,
    packages: packages
  };

  System.config(config);

})(this);

The problem is Visual Studio Code doesn't recognize the imports in my .ts files. It says it couldn't find the module.

Borax answered 17/5, 2016 at 22:9 Comment(4)
Are you trying to import them with the relative paths or globally?Rosaliarosalie
I want to avoid having to do something like import { MyService } from '../../../services'. Answering it: I'm trying to import globally (import { MyService } from 'my-project/services'.Borax
You have a github or something? I ended up having to convert the javascript lib I was using into a TS one and upload it to npm... not the best, but it worked.Rosaliarosalie
This question is precisely what I am attempting to accomplish, but I haven't figured it out either.Chesna
P
7

You need 2 things to accomplish this. First, as other answer pointed out, you need to create "barrels" (index.ts) inside src/app/services, src/app/models and other such folders. So you folder structure looks something like this:

enter image description here

Where the index.ts files contain the exports of stuff that you want to import in your application:

export * from './service1';
export * from './service2';

So far, this will give you the possibility to do this:

import { Service1, Service2 } from '../../services';

However, this is not ideal because of the relative paths like ../../. In order to fix this, you need do one more step: specify baseUrl and paths TypeScript options in tsconfig.json (see https://www.typescriptlang.org/docs/handbook/module-resolution.html).

If you only want to get rid of relative paths, you just need to specify the baseUrl option like this:

tsconfig.json:

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

After this is done, you should be able to import your services like this:

import { Service1, Service2 } from 'app/services';

But if you also need to have import like my-project/services (my-project instead of app), then you also need to specify paths configuration like the following:

{
  "compilerOptions": {
    ...
    "baseUrl": "./src",
    "paths": {
      "my-project/*": [
        "app/*"
      ]      
    }
  }
}

With this configuration the following should work:

import { Service1, Service2 } from 'my-project/services';
Phosphate answered 5/3, 2017 at 13:48 Comment(1)
Interesting enough i already did all this. Turns out the validation of imports done by TSLint extension (or VS Code) lags a bit when changing tsconfig.json. Anyhow, answer is correct, thanks!Vine
T
1

You'll want to create a barrel inside the services folder (for example) which will export all nested exports manually, this way you can import 2 (or more) modules from the barrel, where before you'd have to use 2 different imports.

Last I heard, this breaks tree-shaking for some reason, but I might wrong about this.

Anyway, read up some more here.

Thrall answered 5/3, 2017 at 12:26 Comment(0)
H
0

Edit: closer to what you want

You could generate the declaration files of your app and add them to the @types folder (or create your own and add that to the tsconfig's typeRoots array).

New tsconfig

You could use the console but I would recommend creating a new tsconfig_decl.json file. Sadly I didn't find a way to just create the declarations, therefore you need to clean out the dist folder afterward.

Example tsconfig_decl.json

The important properties are declaration, declarationDir and I guess outDir to know which folder to delete.

{
  "compilerOptions": {
    "baseUrl": "",
    "declaration": true,
    "declarationDir": "../node_modules/@types/myproject",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "lib": ["es6", "dom"],
    "mapRoot": "./",
    "module": "es6",
    "moduleResolution": "node",
    "outDir": "../dist/dec-del",
    "sourceMap": true,
    "target": "es5",

    "typeRoots": [
      "../node_modules/@types"
    ]
  }
}

Generate declarations

tsc -p "src/tsconfig_decl.json"

Provided the folder structures/barrels are correct, you should be able to use

import { AuthService, UserService } from 'myproject/services';

--------------- Old Answer ---------------

If you have this structure

src/app/services
src/app/components
src/app/models
src/app/pipes

you could create index.ts files (barrels) inside these folders that export all available services/components/... . Then you can access them using app/services. It's not entirely what you want but you could rename the app folder to get your project name in there or I guess changing the build process may help with that.

src/app/services/index.ts

export * from './auth.service';
export * from './user.service';
...

Using the services

import { AuthService, UserService } from 'app/services';
... 

If you have a deeper structure you need to create barrels up the chain or use the relative path on the top level to get the path you want.

Handoff answered 5/3, 2017 at 12:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.