Angular 2 Barrels
In Angular 2, I'm trying to get barrels to work as described in the documentation.
在Angular 2中,我正试图让文件中描述的桶工作。
The official Angular 2 style guide talks about using barrels to aggregate and shorten import
statements.
官方的Angular 2风格指南讨论了使用桶来汇总和缩短进口报表。
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.
我发现对于一些桶,我必须在导入时指定索引JavaScript文件名,而我不应该这样做。
Barrel Example
(modify the app/app.component.ts file on line 12)
(修改第12行的app / app.component.ts文件)
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.
在我的实际项目中遇到这个(在ASP.NET下运行)后,我创建了一个Plunker来演示我修改了英雄之旅以使用桶的问题。
In app/app.component
, the basic way to import is like this:
在app / app.component中,导入的基本方法如下:
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'; line表示名称为index.ts的文件,其中包含导出/导入的组件:
// 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?
如何在隐含index.js文件名的地方使用它?
3 个解决方案
#1
8
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
AFAIK SystemJS本身并不了解桶,但Webpack确实如此。顺便说一句,在深入了解Angular为它的模块做了什么之后,我找到了一个解决方案
In system.config.js
you'll need to do the following things
在system.config.js中,您需要执行以下操作
Note: the parent directory of a index.ts
is the barrel
, ICYDK
注意:index.ts的父目录是桶,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
将它们添加到packageNames,就像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而不是package是因为你必须为每个桶重复{main:'index.js',defaultExtension:'js'}(文件名和扩展名),但是角度已经通过循环使用。
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.
两者都有效,但后者无法提供智能感知并显示错误,说无法找到模块'桶'。我没有解决方案。但是当我这样做时,我会添加它。
#2
5
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:
没有必要添加任何东西来映射,因为应该包含所有内容的app已经在那里。我们可以用子包创建一个数组。在我的情况下是:
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
然后修改提供的packIndex函数以获取第二个参数
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.
现在我们可以像包括angular一样将子包添加到packages对象。
ngPackageNames.forEach(name => setPackageConfig(name, '@angular/'));
subPackageNames.forEach(name => packIndex(name, 'app/'));
#3
0
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).
我遵循了A_Singh的想法,但由于我的systemjs.config.js不同,因此我必须根据现在的英雄之旅教程调整它。 (2016年6月3日 - RC1)。
This modification works, and under VisualStudio Code, Intellisense
worked well for me.
这种修改有效,在VisualStudio Code下,Intellisense对我来说效果很好。
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
这里更新了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
最后,这是app.component.ts现在的样子
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.
然而,它会起作用,因为它在技术上不是NodeJS导入的模块,VS Code抱怨它无法找到它。因此,我个人的选择是留下明确的./modules/hero,它帮助我知道它是我的模块之一而不是导入的模块,我很高兴看不到红线。
#1
8
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
AFAIK SystemJS本身并不了解桶,但Webpack确实如此。顺便说一句,在深入了解Angular为它的模块做了什么之后,我找到了一个解决方案
In system.config.js
you'll need to do the following things
在system.config.js中,您需要执行以下操作
Note: the parent directory of a index.ts
is the barrel
, ICYDK
注意:index.ts的父目录是桶,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
将它们添加到packageNames,就像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而不是package是因为你必须为每个桶重复{main:'index.js',defaultExtension:'js'}(文件名和扩展名),但是角度已经通过循环使用。
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.
两者都有效,但后者无法提供智能感知并显示错误,说无法找到模块'桶'。我没有解决方案。但是当我这样做时,我会添加它。
#2
5
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:
没有必要添加任何东西来映射,因为应该包含所有内容的app已经在那里。我们可以用子包创建一个数组。在我的情况下是:
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
然后修改提供的packIndex函数以获取第二个参数
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.
现在我们可以像包括angular一样将子包添加到packages对象。
ngPackageNames.forEach(name => setPackageConfig(name, '@angular/'));
subPackageNames.forEach(name => packIndex(name, 'app/'));
#3
0
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).
我遵循了A_Singh的想法,但由于我的systemjs.config.js不同,因此我必须根据现在的英雄之旅教程调整它。 (2016年6月3日 - RC1)。
This modification works, and under VisualStudio Code, Intellisense
worked well for me.
这种修改有效,在VisualStudio Code下,Intellisense对我来说效果很好。
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
这里更新了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
最后,这是app.component.ts现在的样子
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.
然而,它会起作用,因为它在技术上不是NodeJS导入的模块,VS Code抱怨它无法找到它。因此,我个人的选择是留下明确的./modules/hero,它帮助我知道它是我的模块之一而不是导入的模块,我很高兴看不到红线。