In Angular 2, I'm trying to get barrels to work as described in the documentation.
The official Angular 2 style guide talks about using barrels to aggregate and shorten import
statements.
I'm finding out that for some barrels, I have to specify the index
JavaScript file name on the import when I shouldn't have to.
(modify the app/app.component.ts file on line 12)
After have encountered this in my actual project (running under ASP.NET) I have created a Plunker to demonstrate the problem where I modified the Tour of Heroes to use barrels.
In app/app.component
, the basic way to import is like this:
import { HeroService } from './hero.service'; import { HeroesComponent } from './heroes.component'; import { HeroDetailComponent } from './hero-detail.component';
But, to use a barrel instead, the import definition would look like this:
import { HeroService, HeroesComponent, HeroDetailComponent } from '../app';
The from '../app';
line indicates a file with the name of index.ts
that contain the exported/imported components:
// app/index.ts export * from './hero-detail.component'; export * from './hero.service'; export * from './heroes.component';
But this doesn't work for me in all cases. The only way I've gotten this to work correctly is by explicitly including the index
file name:
import { HeroService, HeroesComponent, HeroDetailComponent } from '../app/index'; // have to indicate 'index'
How can I get this to work where the index.js
file name is implied?
A barrel is a way to rollup exports from several modules into a single convenient module. The barrel itself is a module file that re-exports selected exports of other modules.
We create Barrel files. A barrel is a way to rollup of imports from several files into a single convenient file. So we have a simple project here that shows different modes of transportation.
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
// 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' };
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' } };
packageNames
, just like angularvar 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.
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/'));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With