Как импортировать бочку только по имени папки?
Angular 2 бочки
В Angular 2 я пытаюсь запустить бочки, как описано в документации.
Официальный Angular 2 стиль руководства рассказывает об использовании баррелей для агрегирования и сокращения операторов import
.
Я выясню, что для некоторых баррелей я должен указать имя файла index
JavaScript в импорте, когда мне не нужно.
Пример ствола
(измените файл app/app.component.ts в строке 12)
После того, как вы столкнулись с этим в моем фактическом проекте (работающем под ASP.NET), я создал Plunker, чтобы продемонстрировать проблему, когда я модифицировал Прогулка по героям, чтобы использовать бочки.
В app/app.component
основной способ импорта выглядит так:
import { HeroService } from './hero.service';
import { HeroesComponent } from './heroes.component';
import { HeroDetailComponent } from './hero-detail.component';
Но, чтобы вместо этого использовать бочку, определение импорта будет выглядеть так:
import {
HeroService,
HeroesComponent,
HeroDetailComponent
} from '../app';
Строка from '../app';
указывает файл с именем index.ts
, который содержит экспортированные/импортированные компоненты:
// app/index.ts
export * from './hero-detail.component';
export * from './hero.service';
export * from './heroes.component';
Но это не работает для меня во всех случаях. Единственный способ, которым я правильно работал, - это явно указать имя файла index
:
import {
HeroService,
HeroesComponent,
HeroDetailComponent
} from '../app/index'; // have to indicate 'index'
Как я могу заставить это работать, где подразумевается имя файла index.js
?
Ответы
Ответ 1
AFAIK SystemJS
не понимает бочки самостоятельно, но Webpack
делает.
BTW, после того, как выкопали, как Angular
делает это для своих модулей, я нашел решение
В system.config.js
вам нужно сделать следующее:
Примечание. родительский каталог index.ts
- это 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
(объясняется позже) ##
// 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
, как angular
var packageNames = [
'@angular/common',
...
'@angular/upgrade',
'barrel'
];
И все готово.
##
Почему мы использовали packageNames
вместо packages
, потому что вам нужно будет повторить { main: 'index.js', defaultExtension: 'js' }
(имя файла и расширение) для каждого barrel
, но angular уже делает это путем циклического с этим.
packageNames.forEach(function(pkgName) {
packages[pkgName] = { main: 'index.js', defaultExtension: 'js' };
});
В конечном итоге они добавляются к packages
.
Использование
import {something} from '../../barrel'; // relative path to directory of barrel
и
import {something} from 'barrel'; // name of barrel
Оба работают, но более поздний не может предоставить intellisense
и показывает ошибку, говорящую cannot find module 'barrel'
. На что у меня нет решения. Но я добавлю это, когда буду.
Ответ 2
Все это кажется слишком сложным.
Нет необходимости добавлять что-либо к карте, так как app
, который должен содержать все, уже есть. Мы можем просто создать массив с подпакетами. В моем случае это было:
var subPackageNames = [
'+heroes',
'+heroes/shared',
'+heroes/hero-list',
'+heroes/hero-detail',
'+heroes/hero-dashboard'
];
а затем измените предоставленную packIndex
функцию, чтобы взять второй аргумент
function packIndex(pkgName, baseName) {
packages[baseName+pkgName] = { main: 'index.js', defaultExtension: 'js' };
}
теперь мы можем добавить наши вспомогательные пакеты к объекту пакетов, как это делает angular.
ngPackageNames.forEach(name => setPackageConfig(name, '@angular/'));
subPackageNames.forEach(name => packIndex(name, 'app/'));
Ответ 3
Я следовал за идеей A_Singh, но мне пришлось немного ее адаптировать, так как мой systemjs.config.js
отличается, на основе тур-героев как сейчас. (3 июня 2016 года - RC1).
Эта модификация работает, и в соответствии с кодом VisualStudio Intellisense
работал хорошо для меня.
Я также играл со структурой каталогов, чтобы держать вещи модульными, и вот где баррели имели смысл.
Вот как я изменил проект tour-of-heroes
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
И здесь обновленный 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);
И, наконец, вот как 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';
}
Наконец, вы можете сделать импорт как
import { HeroListComponent, HeroDetailComponent, HeroService } from 'hero';
И он будет работать, однако, поскольку он не является технически импортированным модулем NodeJS, VS Code жалуется, что он не может его найти.
Итак, мой личный выбор - оставить явный ./modules/hero
, он помогает мне понять, что это один из моих модулей, а не импортированный, и я рад не видеть красные строки.