Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to import a barrel by folder name only?

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 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.

Barrel Example

(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?

like image 310
Brad Rem Avatar asked May 06 '16 23:05

Brad Rem


People also ask

What is barrel file?

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.

What is a barrel file in Flutter?

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.


2 Answers

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.

like image 55
Ankit Singh Avatar answered Sep 20 '22 06:09

Ankit Singh


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/')); 
like image 22
MrLoh Avatar answered Sep 21 '22 06:09

MrLoh