How to import a barrel by folder name only?

后端 未结 3 1179

Angular 2 Barrels

In Angular 2, I\'m trying to get barrels to work as described in the documentation.

The official Angular 2 style guide t

相关标签:
3条回答
  • 2020-12-05 05:18

    AFAIK SystemJS doesn't understand barrels by itself but Webpack does. BTW, After digging up how Angular does it for it's modules, I've found a solution


    In system.config.js you'll need to do the following things

    Note: the parent directory of a index.ts is the barrel, ICYDK

    • Add the paths of your barrels

    // map tells the System loader where to look for things
      var map = {
        'app':                        'app', // 'dist',
        'rxjs':                       'node_modules/rxjs',
        'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api',
        '@angular':                   'node_modules/@angular',
    
        'barrel':                 'path/to/your/barrel'
      };
    
    • Don't add barrels to packages (explained later)##

    // packages tells the System loader how to load when no filename and/or no extension
      var packages = {
        'app':                        { main: 'app/boot.js',  defaultExtension: 'js' },
        'rxjs':                       { defaultExtension: 'js' },
        'angular2-in-memory-web-api': { defaultExtension: 'js' }
      };
    
    • Add them to packageNames, just like angular

    var packageNames = [
        '@angular/common',
        ...
        '@angular/upgrade',
    
        'barrel'
      ];
    

    And you're done.


    ##

    Why we used packageNames instead of packages is because you'll have to repeat the { main: 'index.js', defaultExtension: 'js' } (filename and extension) for every barrel, but angular is already doing it by looping with this.

    packageNames.forEach(function(pkgName) {
        packages[pkgName] = { main: 'index.js', defaultExtension: 'js' };
    });
    

    Which is ultimately adding them to packages.


    Usage

    import {something} from '../../barrel'; // relative path to directory of barrel
    

    and

    import {something} from 'barrel'; // name of barrel
    

    Both work, but the later one fails to provide intellisense and shows an error saying cannot find module 'barrel'. Which I don't have a solution for. But I'll add it when I do.

    0 讨论(0)
  • 2020-12-05 05:21

    I followed A_Singh's idea, but had to adapt it a little since my systemjs.config.js is different, based on the tour-of-heroes tutorial as of right now. (June 3, 2016 - RC1).

    This modification works, and under VisualStudio Code, Intellisense worked well for me.

    I have also, been playing with directory structure to keep things modular, and here is where the barrels made sense.

    This is how I have modified the tour-of-heroes project

    app
    |- main.ts
    |- app.component.ts
    |- app.component.css
    |-+ components
    | |-+ dashboard
    |   |- dashboard.component.css
    |   |- dashboard.component.html
    |   |- dashboard.component.ts
    |-+ modules
    | |-+ hero
    |   |- index.ts   <-- //The barrel for the Hero module
    |   |-+ components 
    |   | |-+ detail
    |   | | |- hero-detail.component.css
    |   | | |- hero-detail.component.html
    |   | | |- hero-detail.component.ts
    |   | |-+ list
    |   |   |- hero-list.component.css
    |   |   |- hero-list.component.html
    |   |   |- hero-list.component.ts
    |   |-+ models
    |   | |- hero.model.ts
    |   |-+ services
    |     |- hero.service.ts
    |     |- mock-heroes.ts
    

    And here the updated systemjs.config.js

    (function(global) {
      // map tells the System loader where to look for things
      var map = {
        'app':                        'app', // 'dist',
        '@angular':                   'node_modules/@angular',
        'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api',
        'rxjs':                       'node_modules/rxjs',
    
        // The added barrel map
        'hero':                       'app/modules/hero'
      };
      var packages = {
        'app':                        { main: 'main.js',  defaultExtension: 'js' },
        'rxjs':                       { defaultExtension: 'js' },
        'angular2-in-memory-web-api': { defaultExtension: 'js' },
    
        // The package definition (notice I had to declare index.js)
        'hero':                       { main: 'index.js',  defaultExtension: 'js' }
      };
      var ngPackageNames = [
        'common',
        'compiler',
        'core',
        'http',
        'platform-browser',
        'platform-browser-dynamic',
        'router',
        'router-deprecated',
        'upgrade',
      ];
      ngPackageNames.forEach(function(pkgName) {
        packages['@angular/'+pkgName] = { main: pkgName + '.umd.js', defaultExtension: 'js' };
      });
      var config = {
        map: map,
        packages: packages
      }
      System.config(config);
    })(this);
    

    And finally, here is how the app.component.ts now looks like

    import { Component } from '@angular/core';
    import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from '@angular/router-deprecated';
    
    import { DashboardComponent } from './components/dashboard/dashboard.component';
    import { HeroListComponent, HeroDetailComponent, HeroService } from './modules/hero';
    
    @Component({
        selector:   'my-app',
        directives: [ROUTER_DIRECTIVES],
        providers:  [
            ROUTER_PROVIDERS,
            HeroService
        ],
        template:   `
    <h1>{{title}}</h1>
    <nav>
        <a [routerLink]="['Dashboard']">Dashboard</a>
        <a [routerLink]="['HeroList']">Hero List</a>
    </nav>
    <router-outlet></router-outlet>
    `,
        styleUrls: ['app/app.component.css']
    })
    @RouteConfig([
        {
            path: '/dashboard',
            name: 'Dashboard',
            component: DashboardComponent,
            useAsDefault: true
        },
        {
            path: '/hero-list',
            name: 'HeroList',
            component: HeroListComponent
        },
        {
            path: '/detail/:id',
            name: 'HeroDetail',
            component: HeroDetailComponent
        }
    ])
    export class AppComponent {
        title: string = 'Tour of Heroes';
    }
    

    One final note, you could do the import as

    import { HeroListComponent, HeroDetailComponent, HeroService } from 'hero';
    

    And it will work, however, since it is not technically a NodeJS imported module, VS Code complains that it cannot find it. So, my personal choice is to leave the explicit ./modules/hero, it helps me know that it is one of my modules and not an imported one and I'm happy to not see red lines.

    0 讨论(0)
  • 2020-12-05 05:23

    This is all seems overly complicated.

    There's no need to add anything to map, since app, which should be containing everything is already in there. We can just create an array with the subpackages. In my case that was:

    var subPackageNames = [
            '+heroes',
            '+heroes/shared',
            '+heroes/hero-list',
            '+heroes/hero-detail',
            '+heroes/hero-dashboard'
        ];
    

    and then modify the provided packIndex function to take a second argument

    function packIndex(pkgName, baseName) {
        packages[baseName+pkgName] = { main: 'index.js', defaultExtension: 'js' };
    }
    

    now we can add our sub packages to the packages object just like angular does.

    ngPackageNames.forEach(name => setPackageConfig(name, '@angular/'));
    subPackageNames.forEach(name => packIndex(name, 'app/'));
    
    0 讨论(0)
提交回复
热议问题