docs(aio): final migration from old site

This commit is contained in:
Jesus Rodriguez 2017-04-12 21:53:18 +02:00 committed by Pete Bacon Darwin
parent 8f1359d25f
commit 061475402c
38 changed files with 492 additions and 582 deletions

View File

@ -56,6 +56,7 @@ dist/
# TS to JS
!cb-ts-to-js/js*/**/*.js
cb-ts-to-js/js*/**/system*.js
# webpack
!webpack/**/config/*.js

View File

@ -1,45 +0,0 @@
var templateUrlRegex = /templateUrl\s*:(\s*['"`](.*?)['"`]\s*)/gm;
var stylesRegex = /styleUrls *:(\s*\[[^\]]*?\])/g;
var stringRegex = /(['`"])((?:[^\\]\\\1|.)*?)\1/g;
module.exports.translate = function(load){
var url = document.createElement('a');
url.href = load.address;
var basePathParts = url.pathname.split('/');
basePathParts.pop();
var basePath = basePathParts.join('/');
var baseHref = document.createElement('a');
baseHref.href = this.baseURL;
baseHref = baseHref.pathname;
basePath = basePath.replace(baseHref, '');
load.source = load.source
.replace(templateUrlRegex, function(match, quote, url){
let resolvedUrl = url;
if (url.startsWith('.')) {
resolvedUrl = basePath + url.substr(1);
}
return 'templateUrl: "' + resolvedUrl + '"';
})
.replace(stylesRegex, function(match, relativeUrls) {
var urls = [];
while ((match = stringRegex.exec(relativeUrls)) !== null) {
if (match[2].startsWith('.')) {
urls.push('"' + basePath + match[2].substr(1) + '"');
} else {
urls.push('"' + match[2] + '"');
}
}
return "styleUrls: [" + urls.join(', ') + "]";
});
return load;
};

View File

@ -1,52 +0,0 @@
/**
* System configuration for Angular samples
* Adjust as necessary for your application needs.
*/
(function (global) {
System.config({
paths: {
// paths serve as alias
'npm:': 'node_modules/'
},
// map tells the System loader where to look for things
map: {
// our app is within the app folder
'app': 'app',
// angular bundles
'@angular/animations': 'npm:@angular/animations/bundles/animations.umd.js',
'@angular/animations/browser': 'npm:@angular/animations/bundles/animations-browser.umd.js',
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser/animations': 'npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/router/upgrade': 'npm:@angular/router/bundles/router-upgrade.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
'@angular/upgrade': 'npm:@angular/upgrade/bundles/upgrade.umd.js',
'@angular/upgrade/static': 'npm:@angular/upgrade/bundles/upgrade-static.umd.js',
// other libraries
'rxjs': 'npm:rxjs',
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js'
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
app: {
main: './main.js',
defaultExtension: 'js',
meta: {
'./*.js': {
loader: 'systemjs-angular-loader.js'
}
}
},
rxjs: {
defaultExtension: 'js'
}
}
});
})(this);

View File

@ -1,45 +0,0 @@
var templateUrlRegex = /templateUrl\s*:(\s*['"`](.*?)['"`]\s*)/gm;
var stylesRegex = /styleUrls *:(\s*\[[^\]]*?\])/g;
var stringRegex = /(['`"])((?:[^\\]\\\1|.)*?)\1/g;
module.exports.translate = function(load){
var url = document.createElement('a');
url.href = load.address;
var basePathParts = url.pathname.split('/');
basePathParts.pop();
var basePath = basePathParts.join('/');
var baseHref = document.createElement('a');
baseHref.href = this.baseURL;
baseHref = baseHref.pathname;
basePath = basePath.replace(baseHref, '');
load.source = load.source
.replace(templateUrlRegex, function(match, quote, url){
let resolvedUrl = url;
if (url.startsWith('.')) {
resolvedUrl = basePath + url.substr(1);
}
return 'templateUrl: "' + resolvedUrl + '"';
})
.replace(stylesRegex, function(match, relativeUrls) {
var urls = [];
while ((match = stringRegex.exec(relativeUrls)) !== null) {
if (match[2].startsWith('.')) {
urls.push('"' + basePath + match[2].substr(1) + '"');
} else {
urls.push('"' + match[2] + '"');
}
}
return "styleUrls: [" + urls.join(', ') + "]";
});
return load;
};

View File

@ -1,52 +0,0 @@
/**
* System configuration for Angular samples
* Adjust as necessary for your application needs.
*/
(function (global) {
System.config({
paths: {
// paths serve as alias
'npm:': 'node_modules/'
},
// map tells the System loader where to look for things
map: {
// our app is within the app folder
'app': 'app',
// angular bundles
'@angular/animations': 'npm:@angular/animations/bundles/animations.umd.js',
'@angular/animations/browser': 'npm:@angular/animations/bundles/animations-browser.umd.js',
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser/animations': 'npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/router/upgrade': 'npm:@angular/router/bundles/router-upgrade.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
'@angular/upgrade': 'npm:@angular/upgrade/bundles/upgrade.umd.js',
'@angular/upgrade/static': 'npm:@angular/upgrade/bundles/upgrade-static.umd.js',
// other libraries
'rxjs': 'npm:rxjs',
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js'
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
app: {
main: './main.js',
defaultExtension: 'js',
meta: {
'./*.js': {
loader: 'systemjs-angular-loader.js'
}
}
},
rxjs: {
defaultExtension: 'js'
}
}
});
})(this);

View File

@ -1,45 +0,0 @@
var templateUrlRegex = /templateUrl\s*:(\s*['"`](.*?)['"`]\s*)/gm;
var stylesRegex = /styleUrls *:(\s*\[[^\]]*?\])/g;
var stringRegex = /(['`"])((?:[^\\]\\\1|.)*?)\1/g;
module.exports.translate = function(load){
var url = document.createElement('a');
url.href = load.address;
var basePathParts = url.pathname.split('/');
basePathParts.pop();
var basePath = basePathParts.join('/');
var baseHref = document.createElement('a');
baseHref.href = this.baseURL;
baseHref = baseHref.pathname;
basePath = basePath.replace(baseHref, '');
load.source = load.source
.replace(templateUrlRegex, function(match, quote, url){
let resolvedUrl = url;
if (url.startsWith('.')) {
resolvedUrl = basePath + url.substr(1);
}
return 'templateUrl: "' + resolvedUrl + '"';
})
.replace(stylesRegex, function(match, relativeUrls) {
var urls = [];
while ((match = stringRegex.exec(relativeUrls)) !== null) {
if (match[2].startsWith('.')) {
urls.push('"' + basePath + match[2].substr(1) + '"');
} else {
urls.push('"' + match[2] + '"');
}
}
return "styleUrls: [" + urls.join(', ') + "]";
});
return load;
};

View File

@ -1,52 +0,0 @@
/**
* System configuration for Angular samples
* Adjust as necessary for your application needs.
*/
(function (global) {
System.config({
paths: {
// paths serve as alias
'npm:': 'node_modules/'
},
// map tells the System loader where to look for things
map: {
// our app is within the app folder
'app': 'app',
// angular bundles
'@angular/animations': 'npm:@angular/animations/bundles/animations.umd.js',
'@angular/animations/browser': 'npm:@angular/animations/bundles/animations-browser.umd.js',
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser/animations': 'npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/router/upgrade': 'npm:@angular/router/bundles/router-upgrade.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
'@angular/upgrade': 'npm:@angular/upgrade/bundles/upgrade.umd.js',
'@angular/upgrade/static': 'npm:@angular/upgrade/bundles/upgrade-static.umd.js',
// other libraries
'rxjs': 'npm:rxjs',
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js'
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
app: {
main: './main.js',
defaultExtension: 'js',
meta: {
'./*.js': {
loader: 'systemjs-angular-loader.js'
}
}
},
rxjs: {
defaultExtension: 'js'
}
}
});
})(this);

View File

@ -0,0 +1,116 @@
/* #docregion , quickstart, toh */
/* Master Styles */
h1 {
color: #369;
font-family: Arial, Helvetica, sans-serif;
font-size: 250%;
}
h2, h3 {
color: #444;
font-family: Arial, Helvetica, sans-serif;
font-weight: lighter;
}
body {
margin: 2em;
}
/* #enddocregion quickstart */
body, input[text], button {
color: #888;
font-family: Cambria, Georgia;
}
/* #enddocregion toh */
a {
cursor: pointer;
cursor: hand;
}
button {
font-family: Arial;
background-color: #eee;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
cursor: hand;
}
button:hover {
background-color: #cfd8dc;
}
button:disabled {
background-color: #eee;
color: #aaa;
cursor: auto;
}
/* Navigation link styles */
nav a {
padding: 5px 10px;
text-decoration: none;
margin-right: 10px;
margin-top: 10px;
display: inline-block;
background-color: #eee;
border-radius: 4px;
}
nav a:visited, a:link {
color: #607D8B;
}
nav a:hover {
color: #039be5;
background-color: #CFD8DC;
}
nav a.active {
color: #039be5;
}
/* items class */
.items {
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 24em;
}
.items li {
cursor: pointer;
position: relative;
left: 0;
background-color: #EEE;
margin: .5em;
padding: .3em 0;
height: 1.6em;
border-radius: 4px;
}
.items li:hover {
color: #607D8B;
background-color: #DDD;
left: .1em;
}
.items li.selected {
background-color: #CFD8DC;
color: white;
}
.items li.selected:hover {
background-color: #BBD8DC;
}
.items .text {
position: relative;
top: -3px;
}
.items .badge {
display: inline-block;
font-size: small;
color: white;
padding: 0.8em 0.7em 0 0.7em;
background-color: #607D8B;
line-height: 1em;
position: relative;
left: -1px;
top: -4px;
height: 1.8em;
margin-right: .8em;
border-radius: 4px 0 0 4px;
}
/* #docregion toh */
/* everywhere else */
* {
font-family: Arial, Helvetica, sans-serif;
}

View File

@ -150,7 +150,9 @@
<!-- #docregion phase2-->
<div class="container">
<h1>Hero Form</h1>
<form>
<!-- #docregion template-variable-->
<form #heroForm="ngForm">
<!-- #enddocregion template-variable-->
<!-- #docregion ngModel-2-->
{{diagnostic}}
<div class="form-group">

View File

@ -1,4 +1,4 @@
'use strict'; // necessary for es6 output in node
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor';
@ -64,7 +64,7 @@ describe('Pipes', function () {
});
xit('should support flying heroes (pure) ', function () {
it('should support flying heroes (pure) ', function () {
let nameEle = element(by.css('flying-heroes input[type="text"]'));
let canFlyCheckEle = element(by.css('flying-heroes #can-fly'));
let mutateCheckEle = element(by.css('flying-heroes #mutate'));
@ -90,7 +90,7 @@ describe('Pipes', function () {
});
xit('should support flying heroes (impure) ', function () {
it('should support flying heroes (impure) ', function () {
let nameEle = element(by.css('flying-heroes-impure input[type="text"]'));
let canFlyCheckEle = element(by.css('flying-heroes-impure #can-fly'));
let mutateCheckEle = element(by.css('flying-heroes-impure #mutate'));

View File

@ -48,11 +48,11 @@
<!-- #enddocregion display-none -->
<h4>NgIf with template</h4>
<p>&lt;template&gt; element</p>
<p>&lt;ng-template&gt; element</p>
<!-- #docregion ngif-template -->
<template [ngIf]="hero">
<ng-template [ngIf]="hero">
<div>{{hero.name}}</div>
</template>
</ng-template>
<!-- #enddocregion ngif-template -->
<p>template attribute</p>
@ -140,9 +140,9 @@
<!--#enddocregion inside-ngfor -->
<p class="code">&lt;template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" [ngForTrackBy]="trackById"&gt;</p>
<!--#docregion inside-ngfor -->
<template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" [ngForTrackBy]="trackById">
<ng-template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" [ngForTrackBy]="trackById">
<div [class.odd]="odd">({{i}}) {{hero.name}}</div>
</template>
</ng-template>
<!--#enddocregion inside-ngfor -->
</div>
@ -179,21 +179,21 @@
</div>
<!-- #enddocregion ngswitch-template-attr -->
<h4>NgSwitch with &lt;template&gt;</h4>
<h4>NgSwitch with &lt;ng-template&gt;</h4>
<!-- #docregion ngswitch-template -->
<div [ngSwitch]="hero?.emotion">
<template [ngSwitchCase]="'happy'">
<ng-template [ngSwitchCase]="'happy'">
<happy-hero [hero]="hero"></happy-hero>
</template>
<template [ngSwitchCase]="'sad'">
</ng-template>
<ng-template [ngSwitchCase]="'sad'">
<sad-hero [hero]="hero"></sad-hero>
</template>
<template [ngSwitchCase]="'confused'">
</ng-template>
<ng-template [ngSwitchCase]="'confused'">
<confused-hero [hero]="hero"></confused-hero>
</template >
<template ngSwitchDefault>
</ng-template >
<ng-template ngSwitchDefault>
<unknown-hero [hero]="hero"></unknown-hero>
</template>
</ng-template>
</div>
<!-- #enddocregion ngswitch-template -->
@ -202,9 +202,9 @@
<h2>&lt;template&gt;</h2>
<!-- #docregion template-tag -->
<p>Hip!</p>
<template>
<ng-template>
<p>Hip!</p>
</template>
</ng-template>
<p>Hooray!</p>
<!-- #enddocregion template-tag -->
@ -242,9 +242,9 @@
(A) &lt;p template="myUnless condition" class="code unless"&gt;
</p>
<template [myUnless]="condition">
<ng-template [myUnless]="condition">
<p class="code unless">
(A) &lt;template [myUnless]="condition"&gt;
</p>
</template>
</ng-template>

View File

@ -544,13 +544,13 @@ bindon-ngModel
<!-- NgIf binding with template (no *) -->
<template [ngIf]="currentHero">Add {{currentHero.name}} with template</template>
<ng-template [ngIf]="currentHero">Add {{currentHero.name}} with template</ng-template>
<!-- Does not show because isActive is false! -->
<div>Hero Detail removed from DOM (via template) because isActive is false</div>
<template [ngIf]="isActive">
<ng-template [ngIf]="isActive">
<hero-detail></hero-detail>
</template>
</ng-template>
<!-- #docregion NgIf-3 -->
<!-- isSpecial is true -->

View File

@ -141,7 +141,7 @@ function createComponent() {
comp = fixture.componentInstance;
const injector = fixture.debugElement.injector;
location = injector.get(Location);
location = injector.get(Location) as SpyLocation;
router = injector.get(Router);
router.initialNavigation();
spyOn(injector.get(TwainService), 'getQuote')

View File

@ -34,7 +34,7 @@ describe('Angular async helper', () => {
// Use done. Cannot use setInterval with async or fakeAsync
// See https://github.com/angular/angular/issues/10127
it('should run async test with successful delayed Observable', done => {
it('should run async test with successful delayed Observable', (done: any) => {
const source = Observable.of(true).delay(10);
source.subscribe(
val => actuallyDone = true,

View File

@ -18,7 +18,7 @@ describe('FancyService without the TestBed', () => {
expect(service.getValue()).toBe('real value');
});
it('#getAsyncValue should return async value', done => {
it('#getAsyncValue should return async value', (done: DoneFn) => {
service.getAsyncValue().then(value => {
expect(value).toBe('async value');
done();
@ -26,7 +26,7 @@ describe('FancyService without the TestBed', () => {
});
// #docregion getTimeoutValue
it('#getTimeoutValue should return timeout value', done => {
it('#getTimeoutValue should return timeout value', (done: DoneFn) => {
service = new FancyService();
service.getTimeoutValue().then(value => {
expect(value).toBe('timeout value');
@ -35,7 +35,7 @@ describe('FancyService without the TestBed', () => {
});
// #enddocregion getTimeoutValue
it('#getObservableValue should return observable value', done => {
it('#getObservableValue should return observable value', (done: DoneFn) => {
service.getObservableValue().subscribe(value => {
expect(value).toBe('observable value');
done();

View File

@ -73,7 +73,7 @@ describe('use inject helper in beforeEach', () => {
}));
// Must use done. See https://github.com/angular/angular/issues/10127
it('test should wait for FancyService.getObservableDelayValue', done => {
it('test should wait for FancyService.getObservableDelayValue', (done: DoneFn) => {
service.getObservableDelayValue().subscribe(value => {
expect(value).toBe('observable delay value');
done();
@ -187,20 +187,21 @@ describe('TestBed Component Tests', () => {
expect(selected).toHaveText(hero.name);
});
it('can access the instance variable of an `*ngFor` row', () => {
it('can access the instance variable of an `*ngFor` row component', () => {
const fixture = TestBed.createComponent(IoParentComponent);
const comp = fixture.componentInstance;
const heroName = comp.heroes[0].name; // first hero's name
fixture.detectChanges();
const heroEl = fixture.debugElement.query(By.css('.hero')); // first hero
const ngForRow = fixture.debugElement.query(By.directive(IoComponent)); // first hero ngForRow
const ngForRow = heroEl.parent; // Angular's NgForRow wrapper element
const hero = ngForRow.context['hero']; // the hero object passed into the row
expect(hero.name).toBe(heroName, 'ngRow.context.hero');
// jasmine.any is instance-of-type test.
expect(ngForRow.componentInstance).toEqual(jasmine.any(IoComponent), 'component is IoComp');
const hero = ngForRow.context['$implicit']; // the hero object
expect(hero.name).toBe(comp.heroes[0].name, '1st hero\'s name');
const rowComp = ngForRow.componentInstance;
// jasmine.any is an "instance-of-type" test.
expect(rowComp).toEqual(jasmine.any(IoComponent), 'component is IoComp');
expect(rowComp.hero.name).toBe(heroName, 'component.hero');
});
@ -343,7 +344,7 @@ describe('TestBed Component Tests', () => {
const childComp = el.componentInstance as BankAccountComponent;
expect(childComp).toEqual(jasmine.any(BankAccountComponent));
expect(el.context).toBe(comp, 'context is the parent component');
expect(el.context).toBe(childComp, 'context is the child component');
expect(el.attributes['account']).toBe(childComp.id, 'account attribute');
expect(el.attributes['bank']).toBe(childComp.bank, 'bank attribute');
@ -447,7 +448,7 @@ describe('TestBed Component Overrides:', () => {
// `inject` uses TestBed's injector
inject([FancyService], (s: FancyService) => testBedProvider = s)();
tcProvider = fixture.debugElement.injector.get(FancyService);
tpcProvider = fixture.debugElement.children[0].injector.get(FancyService);
tpcProvider = fixture.debugElement.children[0].injector.get(FancyService) as FakeFancyService;
expect(testBedProvider).not.toBe(tcProvider, 'testBed/tc not same providers');
expect(testBedProvider).not.toBe(tpcProvider, 'testBed/tpc not same providers');

View File

@ -12,7 +12,7 @@ describe('HeroDetailComponent - no TestBed', () => {
let hds: any;
let router: any;
beforeEach( done => {
beforeEach((done: any) => {
expectedHero = new Hero(42, 'Bubba');
activatedRoute = new ActivatedRouteStub();
activatedRoute.testParams = { id: expectedHero.id };
@ -45,7 +45,7 @@ describe('HeroDetailComponent - no TestBed', () => {
expect(router.navigate.calls.any()).toBe(false, 'router.navigate not called yet');
});
it('should navigate when click save resolves', done => {
it('should navigate when click save resolves', (done: any) => {
comp.save();
// waits for async save to complete before navigating
hds.saveHero.calls.first().returnValue

View File

@ -90,7 +90,7 @@ function overrideSetup() {
beforeEach( async(() => {
createComponent();
// get the component's injected HeroDetailServiceSpy
hdsSpy = fixture.debugElement.injector.get(HeroDetailService);
hdsSpy = fixture.debugElement.injector.get(HeroDetailService) as any;
}));
it('should have called `getHero`', () => {

View File

@ -78,7 +78,7 @@ describe('TwainComponent', () => {
// #enddocregion tests
// #docregion done-test
it('should show quote after getQuote promise (done)', done => {
it('should show quote after getQuote promise (done)', (done: any) => {
fixture.detectChanges();
// get the spy promise and wait for it to resolve

View File

@ -1,4 +1,4 @@
'use strict'; // necessary for es6 output in node
'use strict'; // necessary for es6 output in node
import { browser, element, by, protractor } from 'protractor';
@ -53,7 +53,7 @@ describe('User Input Tests', function () {
expect(outputTextEle.getText()).toEqual('a | ab | abc |');
});
xit('should be able to filter key events', () => {
it('should be able to filter key events', () => {
let mainEle = element(by.css('key-up3'));
let inputEle = mainEle.element(by.css('input'));
let outputTextEle = mainEle.element(by.css('p'));

View File

@ -62,7 +62,7 @@ module.exports = {
// Workaround for angular/angular#11580
new webpack.ContextReplacementPlugin(
// The (\\|\/) piece accounts for path separators in *nix and Windows
/angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/,
/angular(\\|\/)core(\\|\/)@angular/,
helpers.root('./src'), // location of your src
{} // a map of your routes
),

View File

@ -6,21 +6,21 @@ var helpers = require('./helpers');
module.exports = webpackMerge(commonConfig, {
devtool: 'cheap-module-eval-source-map',
output: {
path: helpers.root('dist'),
publicPath: 'http://localhost:8080/',
publicPath: '/',
filename: '[name].js',
chunkFilename: '[id].chunk.js'
},
plugins: [
new ExtractTextPlugin('[name].css')
],
devServer: {
historyApiFallback: true,
stats: 'minimal'
}
});
// #enddocregion
// #enddocregion

View File

@ -1079,7 +1079,7 @@ This is especially convenient when you consider that most dependency values are
<p>
What if the dependency value isn't a class? Sometimes the thing you want to inject is a
span string, function, or object.
string, function, or object.
</p>

View File

@ -408,7 +408,7 @@ console:
<code-example format="nocode">
Angular 2 is running in the development mode. Call enableProdMode() to enable the production mode.
Angular is running in the development mode. Call enableProdMode() to enable the production mode.
</code-example>

View File

@ -7,48 +7,48 @@ Render dynamic forms with FormGroup.
@description
We can't always justify the cost and time to build handcrafted forms,
especially if we'll need a great number of them, they're similar to each other, and they change frequently
Building handcrafted forms can be costly and time-consuming,
especially if you need a great number of them, they're similar to each other, and they change frequently
to meet rapidly changing business and regulatory requirements.
It may be more economical to create the forms dynamically, based on metadata that describe the business object model.
It may be more economical to create the forms dynamically, based on
metadata that describes the business object model.
In this cookbook we show how to use `formGroup` to dynamically render a simple form with different control types and validation.
It's a primitive start.
This cookbook shows you how to use `formGroup` to dynamically
render a simple form with different control types and validation.
It's a primitive start.
It might evolve to support a much richer variety of questions, more graceful rendering, and superior user experience.
All such greatness has humble beginnings.
In our example we use a dynamic form to build an online application experience for heroes seeking employment.
The example in this cookbook is a dynamic form to build an
online application experience for heroes seeking employment.
The agency is constantly tinkering with the application process.
We can create the forms on the fly *without changing our application code*.
You can create the forms on the fly *without changing the application code*.
{@a toc}
## Table of contents
[Bootstrap](guide/dynamic-form#bootstrap)
[Question Model](guide/dynamic-form#object-model)
[Form Component](guide/dynamic-form#form-component)
[Questionnaire Metadata](guide/dynamic-form#questionnaire-metadata)
[Dynamic Template](guide/dynamic-form#dynamic-template)
# Contents
* [Bootstrap](guide/dynamic-form#bootstrap)
* [Question model](guide/dynamic-form#object-model)
* [Question form components](guide/dynamic-form#form-component)
* [Questionnaire data](guide/dynamic-form#questionnaire-data)
* [Dynamic template](guide/dynamic-form#dynamic-template)
**See the <live-example name="cb-dynamic-form"></live-example>**.
See the <live-example name="cb-dynamic-form"></live-example>.
{@a bootstrap}
## Bootstrap
We start by creating an `NgModule` called `AppModule`.
Start by creating an `NgModule` called `AppModule`.
In our example we will be using Reactive Forms.
This cookbook uses [reactive forms](guide/reactive-forms).
Reactive Forms belongs to a different `NgModule` called `ReactiveFormsModule`, so in order to access any Reactive Forms directives, we have to import `ReactiveFormsModule` from the `@angular/forms` library.
Reactive forms belongs to a different `NgModule` called `ReactiveFormsModule`,
so in order to access any reactive forms directives, you have to import
`ReactiveFormsModule` from the `@angular/forms` library.
We bootstrap our `AppModule` in main.ts.
Bootstrap the `AppModule` in `main.ts`.
<code-tabs>
@ -66,13 +66,13 @@ We bootstrap our `AppModule` in main.ts.
{@a object-model}
## Question Model
## Question model
The next step is to define an object model that can describe all scenarios needed by the form functionality.
The hero application process involves a form with a lot of questions.
The "question" is the most fundamental object in the model.
The hero application process involves a form with a lot of questions.
The _question_ is the most fundamental object in the model.
We have created `QuestionBase` as the most fundamental question class.
The following `QuestionBase` is a fundamental question class.
<code-example path="cb-dynamic-form/src/app/question-base.ts" title="src/app/question-base.ts">
@ -81,10 +81,13 @@ We have created `QuestionBase` as the most fundamental question class.
From this base we derived two new classes in `TextboxQuestion` and `DropdownQuestion` that represent Textbox and Dropdown questions.
The idea is that the form will be bound to specific question types and render the appropriate controls dynamically.
From this base you can derive two new classes in `TextboxQuestion` and `DropdownQuestion`
that represent textbox and dropdown questions.
The idea is that the form will be bound to specific question types and render the
appropriate controls dynamically.
`TextboxQuestion` supports multiple html5 types like text, email, url etc via the `type` property.
`TextboxQuestion` supports multiple HTML5 types such as text, email, and url
via the `type` property.
<code-example path="cb-dynamic-form/src/app/question-textbox.ts" title="src/app/question-textbox.ts" linenums="false">
@ -102,8 +105,9 @@ The idea is that the form will be bound to specific question types and render th
Next we have defined `QuestionControlService`, a simple service for transforming our questions to a `FormGroup`.
In a nutshell, the form group consumes the metadata from the question model and allows us to specify default values and validation rules.
Next is `QuestionControlService`, a simple service for transforming the questions to a `FormGroup`.
In a nutshell, the form group consumes the metadata from the question model and
allows you to specify default values and validation rules.
<code-example path="cb-dynamic-form/src/app/question-control.service.ts" title="src/app/question-control.service.ts" linenums="false">
@ -113,10 +117,11 @@ In a nutshell, the form group consumes the metadata from the question model and
{@a form-component}
## Question form components
Now that we have defined the complete model we are ready to create components to represent the dynamic form.
Now that you have defined the complete model you are ready
to create components to represent the dynamic form.
`DynamicFormComponent` is the entry point and the main container for the form.
`DynamicFormComponent` is the entry point and the main container for the form.
<code-tabs>
@ -132,9 +137,10 @@ Now that we have defined the complete model we are ready to create components to
It presents a list of questions, each question bound to a `<df-question>` component element.
It presents a list of questions, each bound to a `<df-question>` component element.
The `<df-question>` tag matches the `DynamicFormQuestionComponent`,
the component responsible for rendering the details of each _individual_ question based on values in the data-bound question object.
the component responsible for rendering the details of each _individual_
question based on values in the data-bound question object.
<code-tabs>
@ -151,26 +157,30 @@ the component responsible for rendering the details of each _individual_ questio
Notice this component can present any type of question in our model.
We only have two types of questions at this point but we can imagine many more.
Notice this component can present any type of question in your model.
You only have two types of questions at this point but you can imagine many more.
The `ngSwitch` determines which type of question to display.
In both components we're relying on Angular's **formGroup** to connect the template HTML to the
In both components you're relying on Angular's **formGroup** to connect the template HTML to the
underlying control objects, populated from the question model with display and validation rules.
`formControlName` and `formGroup` are directives defined in `ReactiveFormsModule`. Our templates can access these directives directly since we imported `ReactiveFormsModule` from `AppModule`.
{@a questionnaire-metadata}
`formControlName` and `formGroup` are directives defined in
`ReactiveFormsModule`. The templates can access these directives
directly since you imported `ReactiveFormsModule` from `AppModule`.
{@a questionnaire-data}
## Questionnaire data
`DynamicFormComponent` expects the list of questions in the form of an array bound to `@Input() questions`.
`DynamicFormComponent` expects the list of questions in the form of an array bound to `@Input() questions`.
The set of questions you've defined for the job application is returned from the `QuestionService`.
In a real app you'd retrieve these questions from storage.
The key point is that you control the hero job application questions
entirely through the objects returned from `QuestionService`.
Questionnaire maintenance is a simple matter of adding, updating,
and removing objects from the `questions` array.
The set of questions we have defined for the job application is returned from the `QuestionService`.
In a real app we'd retrieve these questions from storage.
The key point is that we control the hero job application questions entirely through the objects returned from `QuestionService`.
Questionnaire maintenance is a simple matter of adding, updating, and removing objects from the `questions` array.
<code-example path="cb-dynamic-form/src/app/question.service.ts" title="src/app/question.service.ts">
@ -178,7 +188,7 @@ underlying control objects, populated from the question model with display and v
Finally, we display an instance of the form in the `AppComponent` shell.
Finally, display an instance of the form in the `AppComponent` shell.
<code-example path="cb-dynamic-form/src/app/app.component.ts" title="app.component.ts">
@ -188,17 +198,18 @@ Finally, we display an instance of the form in the `AppComponent` shell.
{@a dynamic-template}
## Dynamic Template
Although in this example we're modelling a job application for heroes, there are no references to any specific hero question
outside the objects returned by `QuestionService`.
Although in this example you're modelling a job application for heroes, there are
no references to any specific hero question
outside the objects returned by `QuestionService`.
This is very important since it allows us to repurpose the components for any type of survey
as long as it's compatible with our *question* object model.
The key is the dynamic data binding of metadata used to render the form
without making any hardcoded assumptions about specific questions.
In addition to control metadata, we are also adding validation dynamically.
This is very important since it allows you to repurpose the components for any type of survey
as long as it's compatible with the *question* object model.
The key is the dynamic data binding of metadata used to render the form
without making any hardcoded assumptions about specific questions.
In addition to control metadata, you are also adding validation dynamically.
The *Save* button is disabled until the form is in a valid state.
When the form is valid, we can click *Save* and the app renders the current form values as JSON.
The *Save* button is disabled until the form is in a valid state.
When the form is valid, you can click *Save* and the app renders the current form values as JSON.
This proves that any user input is bound back to the data model.
Saving and retrieving the data is an exercise for another time.

View File

@ -296,6 +296,19 @@ You added a *Submit* button at the bottom with some classes on it for styling.
*You're not using Angular yet*. There are no bindings or extra directives, just layout.
<div class="l-sub-section">
In template driven forms, if you've imported `FormsModule`, you don't have to do anything
to the `<form>` tag in order to make use of `FormsModule`. Continue on to see how this works.
</div>
The `container`, `form-group`, `form-control`, and `btn` classes
come from [Twitter Bootstrap](http://getbootstrap.com/css/). These classes are purely cosmetic.
Bootstrap gives the form a little style.
@ -400,6 +413,42 @@ You left yourself a note to throw it away when you're done.
Focus on the binding syntax: `[(ngModel)]="..."`.
You need one more addition to display the data. Declare
a template variable for the form. Update the `<form>` tag with
`#heroForm="ngForm"` as follows:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="template-variable">
</code-example>
The variable `heroForm` is now a reference to the `NgForm` directive that governs the form as a whole.
<div class="l-sub-section">
### The _NgForm_ directive
What `NgForm` directive?
You didn't add an [NgForm](api/forms/index/NgForm-directive) directive.
Angular did. Angular automatically creates and attaches an `NgForm` directive to the `<form>` tag.
The `NgForm` directive supplements the `form` element with additional features.
It holds the controls you created for the elements with an `ngModel` directive
and `name` attribute, and monitors their properties, including their validity.
It also has its own `valid` property which is true only *if every contained
control* is valid.
</div>
If you ran the app now and started typing in the *Name* input box,
adding and deleting characters, you'd see them appear and disappear
from the interpolated text.
@ -442,7 +491,7 @@ Defining a `name` attribute is a requirement when using `[(ngModel)]` in combina
Internally, Angular creates `FormControl` instances and
registers them with an `NgForm` directive that Angular attached to the `<form>` tag.
Each `FormControl` is registered under the name you assigned to the `name` attribute.
Read more in [The NgForm directive](guide/forms#ngForm), later in this page.
Read more in the previous section, [The NgForm directive](guide/forms#ngForm).
</div>
@ -784,32 +833,9 @@ to the hero form component's `onSubmit()` method:
You added something extra at the end. You defined a
template reference variable, `#heroForm`, and initialized it with the value "ngForm".
The variable `heroForm` is now a reference to the `NgForm` directive that governs the form as a whole.
<div class="l-sub-section">
### The _NgForm_ directive
What `NgForm` directive?
You didn't add an [NgForm](api/forms/index/NgForm-directive) directive.
Angular did. Angular automatically creates and attaches an `NgForm` directive to the `<form>` tag.
The `NgForm` directive supplements the `form` element with additional features.
It holds the controls you created for the elements with an `ngModel` directive
and `name` attribute, and monitors their properties, including their validity.
It also has its own `valid` property which is true only *if every contained
control* is valid.
</div>
You'd already defined a template reference variable,
`#heroForm`, and initialized it with the value "ngForm".
Now, use that variable to access the form with the Submit button.
You'll bind the form's overall validity via

View File

@ -12,24 +12,36 @@ Translate the app's template text into multiple languages.
Angular's _internationalization_ (_i18n_) tools help make your app available in multiple languages.
## Table of contents
# Contents
* [Angular and i18n template translation](guide/i18n#angular-i18n)
* [Mark text with the _i18n_ attribute](guide/i18n#i18n-attribute)
* [Help the translator with a description and intent](guide/i18n#help-translator)
* [Translate text without creating an element](guide/i18n#no-element)
* [Add _i18n-..._ translation attributes](guide/i18n#translate-attributes)
* [Handle singular and plural](guide/i18n#cardinality)
* [Select among alternative texts](guide/i18n#select)
* [Create a translation source file with the **_ng-xi18n_ extraction tool**](guide/i18n#ng-xi18n)
* [Other translation formats](guide/i18n#other-formats)
* [Other options](guide/i18n#ng-xi18n-options)
* [Add an `npm` script for convenience](guide/i18n#npm-i18n-script)
* [Translate text messages](guide/i18n#translate)
* [Create a localization folder](guide/i18n#localization-folder)
* [Translate text nodes](guide/i18n#translate-text-nodes)
* [Translate `plural` and `select`](guide/i18n#translate-plural-select)
* [Translate `plural`](guide/i18n#translate-plural)
* [Translate `select`](guide/i18n#translate-select)
* [The app before translation](guide/i18n#app-pre-translation)
* [Merge the completed translation file into the app](guide/i18n#merge)
* [Merge with the JIT compiler](guide/i18n#jit)
* [SystemJS text plugin](guide/i18n#text-plugin)
* [Create translation providers](guide/i18n#create-translation-providers)
* [Bootstrap the app with translation providers](guide/i18n#bootstrap-the-app)
* [Internationalization with the AOT compiler](guide/i18n#aot)
* [Translation file maintenance and _id_ changes](guide/i18n#maintenance)
**Try this** <live-example name="cb-i18n" title="i18n Example in Spanish">live example</live-example>
Try this <live-example name="cb-i18n" title="i18n Example in Spanish">live example</live-example>
of a JIT-compiled app, translated into Spanish.
@ -40,7 +52,7 @@ of a JIT-compiled app, translated into Spanish.
## Angular and _i18n_ template translation
Application internationalization is a challenging, many-faceted effort that
Application internationalization is a challenging, many-faceted effort that
takes dedication and enduring commitment.
Angular's _i18n_ internationalization facilities can help.
@ -67,10 +79,10 @@ The _i18n_ template translation process has four phases:
1. An angular _i18n_ tool extracts the marked messages into an industry standard translation source file.
1. A translator edits that file, translating the extracted text messages into the target language,
1. A translator edits that file, translating the extracted text messages into the target language,
and returns the file to you.
1. The Angular compiler imports the completed translation files,
1. The Angular compiler imports the completed translation files,
replaces the original messages with translated text, and generates a new version of the application
in the target language.
@ -91,7 +103,7 @@ Place it on every element tag whose fixed text should be translated.
`i18n` is not an Angular _directive_.
`i18n` is not an Angular _directive_.
It's a custom _attribute_, recognized by Angular tools and compilers.
After translation, the compiler removes it.
@ -100,7 +112,7 @@ After translation, the compiler removes it.
In the accompanying sample, an `<h1>` tag displays a simple English language greeting
In the accompanying sample, an `<h1>` tag displays a simple English language greeting
that you translate into Spanish:
<code-example path="cb-i18n/src/app/app.component.1.html" region="greeting" title="src/app/app.component.html" linenums="false">
@ -118,9 +130,12 @@ Add the `i18n` attribute to the tag to mark it for translation.
{@a help-translator}
### Help the translator with a _description_ and _intent_
In order to translate it accurately, the translator may
In order to translate it accurately, the translator may
need a description of the message.
Assign a description to the i18n attribute:
@ -131,8 +146,8 @@ Assign a description to the i18n attribute:
In order to deliver a correct translation, the translator may need to
know your _intent_&mdash;the true _meaning_ of the text
In order to deliver a correct translation, the translator may need to
know your _intent_&mdash;the true _meaning_ of the text
within _this particular_ application context.
In front of the description, add some contextual meaning to the assigned string,
separating it from the description with the `|` character (`<meaning>|<description>`):
@ -144,11 +159,15 @@ separating it from the description with the `|` character (`<meaning>|<descripti
While all appearances of a message with the _same_ meaning have the _same_ translation,
While all appearances of a message with the _same_ meaning have the _same_ translation,
a message with *a variety of possible meanings* could have different translations.
The Angular extraction tool preserves both the _meaning_ and the _description_ in the translation source file
to facilitiate contextually-specific translations.
{@a no-element}
### Translate text without creating an element
Suppose there is a stretch of text that you'd like to translate.
@ -189,9 +208,9 @@ You've added an image to your template. You care about accessibility too so you
The `title` attribute needs to be translated.
The `title` attribute needs to be translated.
Angular i18n support has more translation attributes in the form,`i18n-x`, where `x` is the
name of the attribute to translate.
name of the attribute to translate.
To translate the `title` on the `img` tag from the previous example, write:
@ -213,9 +232,9 @@ You can also assign a meaning and a description with the `i18n-x="<meaning>|<des
Different languages have different pluralization rules.
Suppose your application says something about a collection of wolves.
Suppose your application says something about a collection of wolves.
In English, depending upon the number of wolves, you could display "no wolves", "one wolf", "two wolves", or "a wolf pack".
Other languages might express the _cardinality_ differently.
Other languages might express the _cardinality_ differently.
Here's how you could mark up the component template to display the phrase appropriate to the number of wolves:
@ -226,10 +245,10 @@ Here's how you could mark up the component template to display the phrase approp
* The first parameter is the key. It is bound to the component property (`wolves`)
* The first parameter is the key. It is bound to the component property (`wolves`)
that determines the number of wolves.
* The second parameter identifies this as a `plural` translation type.
* The third parameter defines a pluralization pattern consisting of pluralization
* The third parameter defines a pluralization pattern consisting of pluralization
categories and their matching values.
Pluralization categories include:
@ -244,7 +263,7 @@ Put the default _English_ translation in braces (`{}`) next to the pluralization
* When you're talking about one wolf, you could write `=1 {one wolf}`.
* For zero wolves, you could write `=0 {no wolves}`.
* For two wolves, you could write `=2 {two wolves}`.
* For two wolves, you could write `=2 {two wolves}`.
You could keep this up for three, four, and every other number of wolves.
Or you could specify the **`other`** category as a catch-all for any unmatched cardinality
@ -257,8 +276,8 @@ and write something like: `other {a wolf pack}`.
This syntax conforms to the
<a href="http://userguide.icu-project.org/formatparse/messages" target="_blank" title="ICU Message Format">ICU Message Format</a>
that derives from the
<a href="http://cldr.unicode.org/" target="_blank" title="CLDR">Common Locale Data Repository (CLDR),</a>
that derives from the
<a href="http://cldr.unicode.org/" target="_blank" title="CLDR">Common Locale Data Repository (CLDR)</a>,
which specifies the
<a href="http://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules" target="_blank" title="Pluralization Rules">pluralization rules</a>.
@ -274,12 +293,12 @@ which specifies the
The application displays different text depending upon whether the hero is male or female.
These text alternatives require translation too.
You can handle this with a `select` translation.
You can handle this with a `select` translation.
A `select` also follows the
<a href="http://userguide.icu-project.org/formatparse/messages" target="_blank" title="ICU Message Format">ICU message syntax</a>.
<a href="http://userguide.icu-project.org/formatparse/messages" target="_blank" title="ICU Message Format">ICU message syntax</a>.
You choose among alternative translation based on a string value instead of a number.
The following format message in the component template binds to the component's `gender`
The following format message in the component template binds to the component's `gender`
property, which outputs either an "m" or an "f".
The message maps those values to the appropriate translation:
@ -296,7 +315,7 @@ The message maps those values to the appropriate translation:
## Create a translation source file with the _ng-xi18n_ tool
Use the **_ng-xi18n_ extraction tool** to extract the `i18n`-marked texts
Use the **_ng-xi18n_ extraction tool** to extract the `i18n`-marked texts
into a translation source file in an industry standard format.
This is an Angular CLI tool in the `@angular/compiler-cli` npm package.
@ -340,7 +359,7 @@ By default, the tool generates a translation file named **`messages.xlf`** in th
### Other translation formats
You can generate a file named **`messages.xmb`** in the
You can generate a file named **`messages.xmb`** in the
<a href="http://cldr.unicode.org/development/development-process/design-proposals/xmb" target="_blank">XML Message Bundle (XMB)</a> format
by adding the `--i18nFormat=xmb` flag.
@ -359,8 +378,8 @@ This sample sticks with the _XLIFF_ format.
### Other options
You may have to specify additional options.
For example, if the `tsconfig.json` TypeScript configuration
You may have to specify additional options.
For example, if the `tsconfig.json` TypeScript configuration
file is located somewhere other than in the root folder,
you must identify the path to it with the `-p` option:
@ -383,7 +402,7 @@ to make the command easier to remember and run:
<code-example format='.' language='sh'>
"scripts": {
"i18n": "ng-xi18n",
"i18n": "ng-xi18n",
...
}
</code-example>
@ -400,7 +419,7 @@ Now you can issue command variations such as these:
Note the `--` flag before the options.
Note the `--` flag before the options.
It tells _npm_ to pass every flag thereafter to `ng-xi18n`.
@ -410,9 +429,9 @@ It tells _npm_ to pass every flag thereafter to `ng-xi18n`.
## Translate text messages
The `ng-xi18n` command generates a translation source file
The `ng-xi18n` command generates a translation source file
in the project root folder named `messages.xlf`.
The next step is to translate the English language template
The next step is to translate the English language template
text into the specific language translation
files. The cookbook sample creates a Spanish translation file.
@ -425,14 +444,14 @@ files. The cookbook sample creates a Spanish translation file.
You will probably translate into more than one other language so it's a good idea
for the project structure to reflect your entire internationalization effort.
One approach is to dedicate a folder to localization and store related assets
(for example, internationalization files) there.
One approach is to dedicate a folder to localization and store related assets,
such as internationalization files, there.
<div class="l-sub-section">
Localization and internationalization are
Localization and internationalization are
<a href="https://en.wikipedia.org/wiki/Internationalization_and_localization" target="_blank">different but closely related terms</a>.
</div>
@ -443,10 +462,13 @@ This cookbook follows that suggestion. It has a `locale` folder under the `src/`
Assets within the folder carry a filename extension that matches a language-culture code from a
<a href="https://msdn.microsoft.com/en-us/library/ee825488(v=cs.20).aspx" target="_blank">well-known codeset</a>.
Make a copy of the `messages.xlf` file, put it in the `locale` folder and
Make a copy of the `messages.xlf` file, put it in the `locale` folder and
rename it `messages.es.xlf`for the Spanish language translation.
Do the same for each target language.
{@a translate-text-nodes}
### Translate text nodes
In the real world, you send the `messages.es.xlf` file to a Spanish translator who fills in the translations
using one of the
@ -462,7 +484,7 @@ Open `messages.es.xlf` and find the first `<trans-unit>` section:
This XML element represents the translation of the `<h1>` greeting tag you marked with the `i18n` attribute.
This XML element represents the translation of the `<h1>` greeting tag you marked with the `i18n` attribute.
Using the _source_, _description_, and _meaning_ elements to guide your translation,
replace the `<target/>` tag with the Spanish greeting:
@ -502,16 +524,19 @@ Translate the other text nodes the same way:
## Translate _plural_ and _select_
Translating _plural_ and _select_ messages is a little tricky.
The `<source>` tag is empty for `plural` and `select` translation
The `<source>` tag is empty for `plural` and `select` translation
units, which makes them hard to correlate with the original template.
The `XLIFF` format doesn't yet support the ICU rules; it soon will.
The `XLIFF` format doesn't yet support the ICU rules.
However, the `XMB` format does support the ICU rules.
You'll just have to look for them in relation to other translation units that you recognize from elsewhere in the source template.
In this example, you know the translation unit for the `select` must be just below the translation unit for the logo.
### Translate _plural_
{@a translate-plural}
### Translate _plural_
To translate a `plural`, translate its ICU format match values:
@ -521,7 +546,10 @@ To translate a `plural`, translate its ICU format match values:
### Translate _select_
{@a translate-select}
### Translate _select_
The `select` behaves a little differently. Here again is the ICU format message in the component template:
@ -534,7 +562,7 @@ The `select` behaves a little differently. Here again is the ICU format message
The extraction tool broke that into _two_ translation units.
The first unit contains the text that was _outside_ the `select`.
In place of the `select` is a placeholder, `<x id="ICU">`, that represents the `select` message.
In place of the `select` is a placeholder, `<x id="ICU">`, that represents the `select` message.
Translate the text and leave the placeholder where it is.
@ -568,7 +596,7 @@ Here they are together, after translation:
The entire template translation is complete. It's
The entire template translation is complete. It's
time to incorporate that translation into the application.
@ -617,21 +645,21 @@ When the previous steps finish, the sample app _and_ its translation file are as
To merge the translated text into component templates,
compile the application with the completed translation file.
The process is the same whether the file is in `.xlf` format or
in another format (`.xlif` and `.xtb`) that Angular understands.
The process is the same whether the file is in `.xlf` format or
in another format that Angular understands, such as `.xlif` or `.xtb`.
You provide the Angular compiler with three new pieces of information:
* the translation file
* the translation file format
* the <a href="https://en.wikipedia.org/wiki/XLIFF" target="_blank">_Locale ID_</a>
(`es` or `en-US` for instance)
* The translation file.
* The translation file format.
* The <a href="https://en.wikipedia.org/wiki/XLIFF" target="_blank">_Locale ID_</a>
(`es` or `en-US` for instance).
_How_ you provide this information depends upon whether you compile with
the JIT (_Just-in-Time_) compiler or the AOT (_Ahead-of-Time_) compiler.
the JIT (_Just-in-Time_) compiler or the AOT (_Ahead-of-Time_) compiler.
* With [JIT](guide/i18n#jit), you provide the information at bootstrap time.
* With [AOT](guide/i18n#aot), you pass the information as `ngc` options.
* With [AOT](guide/i18n#aot), you pass the information as `ngc` options.
{@a jit}
@ -663,7 +691,7 @@ in the `index.html`.
{@a text-plugin}
### SystemJS Text plugin
### SystemJS text plugin
Notice the SystemJS mapping of `text` to a `systemjs-text-plugin.js`.
With the help of a text plugin, SystemJS can read any file as raw text and
@ -679,16 +707,19 @@ Create the following `systemjs-text-plugin.js` in the `src/` folder:
{@a create-translation-providers}
### Create translation providers
Three providers tell the JIT compiler how to translate the template texts for a particular language
while compiling the application:
* `TRANSLATIONS` is a string containing the content of the translation file.
* `TRANSLATIONS_FORMAT` is the format of the file: `xlf`, `xlif` or `xtb`.
* `TRANSLATIONS_FORMAT` is the format of the file: `xlf`, `xlif`, or `xtb`.
* `LOCALE_ID` is the locale of the target language.
The `getTranslationProviders` function in the following `src/app/i18n-providers.ts`
The `getTranslationProviders()` function in the following `src/app/i18n-providers.ts`
creates those providers based on the user's _locale_
and the corresponding translation file:
@ -704,22 +735,25 @@ and the corresponding translation file:
The function returns an empty `noProviders` array as a `Promise`.
It must return a `Promise` because this function could read a translation file asynchronously from the server.
1. It creates a transaction filename from the locale according to the name and location convention
1. It creates a transaction filename from the locale according to the name and location convention
[described earlier](guide/i18n#localization-folder).
1. The `getTranslationsWithSystemJs` method reads the translation and returns the contents as a string.
Notice that it appends `!text` to the filename, telling SystemJS to use the [text plugin](guide/i18n#text-plugin).
1. The `getTranslationsWithSystemJs()` method reads the translation and returns the contents as a string.
Notice that it appends `!text` to the filename, telling SystemJS to use the [text plugin](guide/i18n#text-plugin).
1. The callback composes a providers array with the three translation providers.
1. Finally, `getTranslationProviders` returns the entire effort as a promise.
1. Finally, `getTranslationProviders()` returns the entire effort as a promise.
{@a bootstrap-the-app}
### Bootstrap the app with translation providers
The Angular `bootstrapModule` method has a second, _options_ parameter
The Angular `bootstrapModule()` method has a second _options_ parameter
that can influence the behavior of the compiler.
You'll create an _options_ object with the translation providers from `getTranslationProviders`
You'll create an _options_ object with the translation providers from `getTranslationProviders()`
and pass it to `bootstrapModule`.
Open the `src/main.ts` and modify the bootstrap code as follows:
@ -729,7 +763,7 @@ Open the `src/main.ts` and modify the bootstrap code as follows:
Notice that it waits for the `getTranslationProviders` promise to resolve before
Notice that it waits for the `getTranslationProviders()` promise to resolve before
bootstrapping the app.
The app is now _internationalized_ for English and Spanish and there is a clear path for adding
@ -740,40 +774,40 @@ more languages.
### _Internationalize_ with the AOT compiler
### _Internationalization_ with the AOT compiler
The JIT compiler translates the application into the target language
The JIT compiler translates the application into the target language
while compiling dynamically in the browser.
That's flexible but may not be fast enough for your users.
That's flexible but may not be fast enough for your users.
The AOT (_Ahead-of-Time_) compiler is part of a build process that
The AOT (_Ahead-of-Time_) compiler is part of a build process that
produces a small, fast, ready-to-run application package.
When you internationalize with the AOT compiler, you pre-build
When you internationalize with the AOT compiler, you pre-build
a separate application package for each
language. Then in the host web page (`index.html`),
language. Then in the host web page, in this case `index.html`,
you determine which language the user needs
and serve the appropriate application package.
This cookbook doesn't cover how to build multiple application packages and
This cookbook doesn't cover how to build multiple application packages and
serve them according to the user's language preference.
It does explain the few steps necessary to tell the AOT compiler to apply a translations file.
Internationalization with the AOT compiler requires
some setup specifically for AOT compilation.
Start with the application project as shown
Internationalization with the AOT compiler requires
some setup specifically for AOT compilation.
Start with the application project as shown
[just before merging the translation file](guide/i18n#app-pre-translation)
and refer to the [AOT cookbook](guide/aot-compiler) to make it _AOT-ready_.
Next, issue an `ngc` compile command for each supported language (including English).
Next, issue an `ngc` compile command for each supported language, including English.
The result is a separate version of the application for each language.
Tell AOT how to translate by adding three options to the `ngc` command:
* `--i18nFile`: the path to the translation file
* `--locale`: the name of the locale
* `--i18nFormat`: the format of the localization file
* `--i18nFile`: the path to the translation file.
* `--locale`: the name of the locale.
* `--i18nFormat`: the format of the localization file.
For this sample, the Spanish language command would be
For this sample, the Spanish language command would be:
<code-example language="sh" class="code-shell">
./node_modules/.bin/ngc --i18nFile=./locale/messages.es.xlf --locale=es --i18nFormat=xlf
@ -803,19 +837,19 @@ Windows users may have to quote the command:
## Translation file maintenance and _id_ changes
As the application evolves, you will change the _i18n_ markup
As the application evolves, you will change the _i18n_ markup
and re-run the `ng-xi18n` extraction tool many times.
The _new_ markup that you add is not a problem;
but _most_ changes to _existing_ markup trigger
generation of _new_ `id`s for the affected translation units.
but _most_ changes to existing markup trigger
generation of new `id`s for the affected translation units.
After an `id` changes, the translation files are no longer in-sync.
**All translated versions of the application will fail** during re-compilation.
The error messages identify the old `id`s that are no longer valid but
After an `id` changes, the translation files are no longer in sync.
**All translated versions of the application will fail** during re-compilation.
The error messages identify the old `id`s that are no longer valid but
they don't tell you what the new `id`s should be.
**Commit all translation message files to source control**,
**Commit all translation message files to source control**,
especially the English source `messages.xlf`.
The difference between the old and the new `messages.xlf` file
help you find and update `id` changes across your translation files.

View File

@ -156,7 +156,7 @@ Then you'll learn about the [Angular form classes](guide/reactive-forms#essentia
## Setup
Follow the steps in the [_Setup_ guide](setup "Setup guide")
Follow the steps in the [_Setup_ guide](guide/setup "Setup guide")
for creating a new project folder (perhaps called `reactive-forms`)
based on the _QuickStart seed_.
@ -989,7 +989,7 @@ by binding to its `hero` input property.
In this approach, the value of `hero` in the `HeroDetailComponent` changes
every time the user selects a new hero.
You should call _setValue_ in the [ngOnChanges](guide/lifecyle-hooks#onchanges)
You should call _setValue_ in the [ngOnChanges](guide/lifecycle-hooks#onchanges)
hook, which Angular calls whenever the input `hero` property changes
as the following steps demonstrate.

View File

@ -10,10 +10,10 @@ Setting the document or window title using the Title service.
{@a top}
Our app should be able to make the browser title bar say whatever we want it to say.
Your app should be able to make the browser title bar say whatever you want it to say.
This cookbook explains how to do it.
**See the <live-example name="cb-set-document-title"></live-example>**.
See the <live-example name="cb-set-document-title"></live-example>.
<table>
@ -49,11 +49,11 @@ The obvious approach is to bind a property of the component to the HTML `<title>
Sorry but that won't work.
The root component of our application is an element contained within the `<body>` tag.
The root component of the application is an element contained within the `<body>` tag.
The HTML `<title>` is in the document `<head>`, outside the body, making it inaccessible to Angular data binding.
We could grab the browser `document` object and set the title manually.
That's dirty and undermines our chances of running the app outside of a browser someday.
You could grab the browser `document` object and set the title manually.
That's dirty and undermines your chances of running the app outside of a browser someday.
<div class="l-sub-section">
@ -74,10 +74,10 @@ Fortunately, Angular bridges the gap by providing a `Title` service as part of t
The [Title](api/platform-browser/index/Title-class) service is a simple class that provides an API
for getting and setting the current HTML document title:
* `getTitle() : string` &mdash; Gets the title of the current HTML document.
* `setTitle( newTitle : string )` &mdash; Sets the title of the current HTML document.
* `getTitle() : string`&mdash;Gets the title of the current HTML document.
* `setTitle( newTitle : string )`&mdash;Sets the title of the current HTML document.
Let's inject the `Title` service into the root `AppComponent` and expose a bindable `setTitle` method that calls it:
You can inject the `Title` service into the root `AppComponent` and expose a bindable `setTitle` method that calls it:
<code-example path="cb-set-document-title/src/app/app.component.ts" region="class" title="src/app/app.component.ts (class)" linenums="false">
@ -86,7 +86,7 @@ Let's inject the `Title` service into the root `AppComponent` and expose a binda
We bind that method to three anchor tags and, voilà!
Bind that method to three anchor tags and voilà!
<figure class='image-display'>
<img src="assets/images/cookbooks/set-document-title/set-title-anim.gif" alt="Set title"></img>
@ -94,7 +94,7 @@ We bind that method to three anchor tags and, voilà!
Here's the complete solution
Here's the complete solution:
<code-tabs>
@ -116,17 +116,18 @@ Here's the complete solution
## Why we provide the *Title* service in *bootstrap*
## Why provide the *Title* service in *bootstrap*
We generally recommended providing application-wide services in the root application component, `AppComponent`.
Generally you want to provide application-wide services in the root application component, `AppComponent`.
Here we recommend registering the title service during bootstrapping,
a location we reserve for configuring the runtime Angular environment.
This cookbook recommends registering the title service during bootstrapping,
a location you reserve for configuring the runtime Angular environment.
That's exactly what we're doing.
That's exactly what you're doing.
The `Title` service is part of the Angular *browser platform*.
If we bootstrap our application into a different platform,
we'll have to provide a different `Title` service that understands the concept of a "document title" for that specific platform.
Ideally the application itself neither knows nor cares about the runtime environment.
If you bootstrap your application into a different platform,
you'll have to provide a different `Title` service that understands
the concept of a "document title" for that specific platform.
Ideally, the application itself neither knows nor cares about the runtime environment.
[Back to top](guide/set-document-title#top)

View File

@ -32,10 +32,11 @@ how you can write your own structural directives to do the same thing.
* [Inside the *NgSwitch* directives](guide/structural-directives#ngSwitch)
* [Prefer the (*) prefix](guide/structural-directives#prefer-asterisk)
* [The &lt;template> element](guide/structural-directives#template)
* [The &lt;ng-template> element](guide/structural-directives#template)
* [Group sibling elements with &lt;ng-container&gt;](guide/structural-directives#ng-container)
* [Write a structural directive](guide/structural-directives#unless)
Try the <live-example></live-example>.
@ -67,7 +68,7 @@ No brackets. No parentheses. Just `*ngIf` set to a string.
You'll learn in this guide that the [asterisk (*) is a convenience notation](guide/structural-directives#asterisk)
and the string is a [_microsyntax_](guide/structural-directives#microsyntax) rather than the usual
[template expression](guide/template-syntax#template-expressions).
Angular desugars this notation into a marked-up `<template>` that surrounds the
Angular desugars this notation into a marked-up `<ng-template>` that surrounds the
host element and its descendents.
Each structural directive does something different with that template.
@ -163,7 +164,7 @@ Confirm that fact using browser developer tools to inspect the DOM.
The top paragraph is in the DOM. The bottom, disused paragraph is not;
in its place is a comment about "template bindings" (more about that [later](guide/structural-directives#asterisk)).
in its place is a comment about "bindings" (more about that [later](guide/structural-directives#asterisk)).
When the condition is false, `NgIf` removes its host element from the DOM,
detaches it from DOM events (the attachments that it made),
@ -243,7 +244,7 @@ First, it translates the `*ngIf="..."` into a template _attribute_, `template="n
Then it translates the template _attribute_ into a template _element_, wrapped around the host element, like this.
Then it translates the template _attribute_ into a `<ng-template>` _element_, wrapped around the host element, like this.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngif-template)" region="ngif-template">
@ -252,8 +253,8 @@ Then it translates the template _attribute_ into a template _element_, wrapped a
* The `*ngIf` directive moved to the `<template>` element where it became a property binding,`[ngIf]`.
* The rest of the `<div>`, including its class attribute, moved inside the `<template>` element.
* The `*ngIf` directive moved to the `<ng-template>` element where it became a property binding,`[ngIf]`.
* The rest of the `<div>`, including its class attribute, moved inside the `<ng-template>` element.
None of these forms are actually rendered.
Only the finished product ends up in the DOM.
@ -265,8 +266,8 @@ Only the finished product ends up in the DOM.
Angular consumed the `<template>` content during its actual rendering and
replaced the `<template>` with a diagnostic comment.
Angular consumed the `<ng-template>` content during its actual rendering and
replaced the `<ng-template>` with a diagnostic comment.
The [`NgFor`](guide/structural-directives#ngFor) and [`NgSwitch...`](guide/structural-directives#ngSwitch) directives follow the same pattern.
@ -278,7 +279,7 @@ The [`NgFor`](guide/structural-directives#ngFor) and [`NgSwitch...`](guide/struc
## Inside _*ngFor_
Angular transforms the `*ngFor` in similar fashion from asterisk (*) syntax through
template _attribute_ to template _element_.
template _attribute_ to `<ng-template>` _element_.
Here's a full-featured application of `NgFor`, written all three ways:
@ -301,7 +302,7 @@ You enable these features in the string assigned to `ngFor`, which you write in
Everything _outside_ the `ngFor` string stays with the host element
(the `<div>`) as it moves inside the `<template>`.
(the `<div>`) as it moves inside the `<ng-template>`.
In this example, the `[ngClass]="odd"` stays on the `<div>`.
@ -315,7 +316,7 @@ In this example, the `[ngClass]="odd"` stays on the `<div>`.
### Microsyntax
The Angular microsyntax lets you configure a directive in a compact, friendly string.
The microsyntax parser translates that string into attributes on the `<template>`:
The microsyntax parser translates that string into attributes on the `<ng-template>`:
* The `let` keyword declares a [_template input variable_](guide/structural-directives#template-input-variable)
that you reference within the template. The input variables in this example are `hero`, `i`, and `odd`.
@ -342,7 +343,10 @@ which `NgFor` has initialized with the hero for the current iteration.
describes additional `NgFor` directive properties and context properties.
These microsyntax mechanisms are available to you when you write your own structural directives.
Studying the source code for `NgIf` and `NgFor` is a great way to learn more.
Studying the
[source code for `NgIf`](https://github.com/angular/angular/blob/master/packages/common/src/directives/ng_if.ts "Source: NgIf")
and [`NgFor`](https://github.com/angular/angular/blob/master/packages/common/src/directives/ng_for_of.ts "Source: NgFor")
is a great way to learn more.
@ -446,7 +450,7 @@ can be desugared into the template _attribute_ form.
That, in turn, can be desugared into the `<template>` element form.
That, in turn, can be desugared into the `<ng-template>` element form.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (ngswitch-template)" region="ngswitch-template">
@ -465,22 +469,21 @@ Use [&lt;ng-container&gt;](guide/structural-directives#ng-container) when there'
to host the directive.
While there's rarely a good reason to apply a structural directive in template _attribute_ or _element_ form,
it's still important to know that Angular creates a `<template>` and to understand how it works.
You'll refer to the `<template>` when you [write your own structural directive](guide/structural-directives#unless).
it's still important to know that Angular creates a `<ng-template>` and to understand how it works.
You'll refer to the `<ng-template>` when you [write your own structural directive](guide/structural-directives#unless).
{@a template}
## The *&lt;template&gt;*
## The *&lt;ng-template&gt;*
The <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template" target="_blank" title="MDN: Template Tag">HTML 5 &lt;template&gt;</a>
is a formula for rendering HTML.
The &lt;ng-template&gt; is an Angular element for rendering HTML.
It is never displayed directly.
In fact, before rendering the view, Angular _replaces_ the `<template>` and its contents with a comment.
In fact, before rendering the view, Angular _replaces_ the `<ng-template>` and its contents with a comment.
If there is no structural directive and you merely wrap some elements in a `<template>`,
If there is no structural directive and you merely wrap some elements in a `<ng-template>`,
those elements disappear.
That's the fate of the middle "Hip!" in the phrase "Hip! Hip! Hooray!".
@ -500,7 +503,7 @@ Angular erases the middle "Hip!", leaving the cheer a bit less enthusiastic.
A structural directive puts a `<template>` to work
A structural directive puts a `<ng-template>` to work
as you'll see when you [write your own structural directive](guide/structural-directives#unless).
@ -708,11 +711,11 @@ Angular's own directives do not.
A simple structural directive like this one creates an
[_embedded view_](api/core/index/EmbeddedViewRef-class "API: EmbeddedViewRef")
from the Angular-generated `<template>` and inserts that view in a
from the Angular-generated `<ng-template>` and inserts that view in a
[_view container_](api/core/index/ViewContainerRef-class "API: ViewContainerRef")
adjacent to the directive's original `<p>` host element.
You'll acquire the `<template>` contents with a
You'll acquire the `<ng-template>` contents with a
[`TemplateRef`](api/core/index/TemplateRef-class "API: TemplateRef")
and access the _view container_ through a
[`ViewContainerRef`](api/core/index/ViewContainerRef-class "API: ViewContainerRef").
@ -839,7 +842,7 @@ You learned
* that structural directives manipulate HTML layout.
* to use [`<ng-container>`](guide/structural-directives#ngcontainer) as a grouping element when there is no suitable host element.
* that the Angular desugars [asterisk (*) syntax](guide/structural-directives#asterisk) into a `<template>`.
* that the Angular desugars [asterisk (*) syntax](guide/structural-directives#asterisk) into a `<ng-template>`.
* how that works for the `NgIf`, `NgFor` and `NgSwitch` built-in directives.
* about the [_microsyntax_](guide/structural-directives#microsyntax) that expands into a [`<template>`](guide/structural-directives#template).
* about the [_microsyntax_](guide/structural-directives#microsyntax) that expands into a [`<ng-template>`](guide/structural-directives#template).
* to write a [custom structural directive](guide/structural-directives#unless), `UnlessDirective`.

View File

@ -3028,11 +3028,11 @@ module are referenced across the entire application.
<div class="s-rule do">
<div class="s-rule avoid">
**Do** not provide services in shared modules. Services are usually
**Avoid** providing services in shared modules. Services are usually
singletons that are provided once for the entire application or
in a particular feature module.
@ -3949,7 +3949,7 @@ in those editors that support it; it won't help with CSS styles.
<div class="s-rule do">
<div class="s-rule consider">

View File

@ -2028,7 +2028,7 @@ The string `"let hero of heroes"` means:
> *Take each hero in the `heroes` array, store it in the local `hero` looping variable, and
make it available to the templated HTML for each iteration.*
Angular translates this instruction into a `<template>` around the host element,
Angular translates this instruction into a `<ng-template>` around the host element,
then uses this template repeatedly to create a new set of elements and bindings for each `hero`
in the list.

View File

@ -157,7 +157,7 @@ Add these properties near the top of the `app.component.ts` file, just below the
In the `Hero` class, refactor the component's `hero` property to be of type `Hero`,
In the `AppComponent` class, refactor the component's `hero` property to be of type `Hero`,
then initialize it with an `id` of `1` and the name `Windstorm`.

View File

@ -163,7 +163,7 @@ Now you can fill the template with hero names.
### List heroes with ngFor
The goal is to bind the array of `heroes` in the component to the template, iterate over them,
The goal is to bind the array of heroes in the component to the template, iterate over them,
and display them individually.
Modify the `<li>` tag by adding the built-in directive `*ngFor`.
@ -255,7 +255,7 @@ In this case, the _master_ is the heroes list and the _detail_ is the selected h
Next you'll connect the master to the detail through a `selectedHero` component property,
which is bound to a click event.
### Add a click event
### Handle click events
Add a click event binding to the `<li>` like this:
@ -309,7 +309,7 @@ Add an `onSelect()` method that sets the `selectedHero` property to the `hero` t
The template still refers to the old `hero` property.
Bind to the new selectedHero property instead as follows:
Bind to the new `selectedHero` property instead as follows:
@ -321,7 +321,8 @@ Bind to the new selectedHero property instead as follows:
### Hide the empty detail with ngIf
When the app loads, the `selectedHero` is undefined and won't be defined until you click a hero's name.
When the app loads, `selectedHero` is undefined.
The selected hero is initialized when the user clicks a hero's name.
Angular can't display properties of the undefined `selectedHero` and throws the following error,
visible in the browser's console:
@ -360,7 +361,7 @@ Don't forget the asterisk (`*`) in front of `ngIf`.
The app no longer fails and the list of names displays again in the browser.
When there is no `selectedHero`, the `ngIf` directive removes the hero detail HTML from the DOM.
When there is no selected hero, the `ngIf` directive removes the hero detail HTML from the DOM.
There are no hero detail elements or bindings to worry about.
When the user picks a hero, `selectedHero` becomes defined and
@ -461,5 +462,5 @@ Your app should look like this <live-example></live-example>.
## The road ahead
You've expanded the Tour of Heroes app, but it's far from complete.
You can't put the entire app into a single component.
In the [next page](tutorial/toh-pt3), you'll split the app into sub-components and make them work together.
An app shouldn't be one monolithic component.
In the [next page](tutorial/toh-pt3), you'll split the app into subcomponents and make them work together.

View File

@ -276,7 +276,7 @@ This module declares only the two application components, `AppComponent` and `He
Read more about Angular modules in the [NgModules](guide/ngmodule "Angular Modules (NgModule) guide.
Read more about Angular modules in the [NgModules](guide/ngmodule "Angular Modules (NgModule)") guide.
</div>
@ -305,7 +305,7 @@ Coordinate the master `AppComponent` with the `HeroDetailComponent`
by binding the `selectedHero` property of the `AppComponent`
to the `hero` property of the `HeroDetailComponent`.
<code-example path="toh-3/app/app.component.1.html" region="hero-detail-binding" title="app.component.html (excerpt)" linenums="false">
<code-example path="toh-3/app/app.component.1.html" region="hero-detail-binding" title="app.component.ts (excerpt)" linenums="false">
</code-example>

View File

@ -1066,8 +1066,8 @@ by telling the router where to go.
This approach requires the following changes to the component class:
1. Import the `router` from the Angular router library.
1. Inject the `router` in the constructor, along with the `HeroService`.
1. Import the `Router` from the Angular router library.
1. Inject the `Router` in the constructor, along with the `HeroService`.
1. Implement `gotoDetail()` by calling the router `navigate()` method.
@ -1223,7 +1223,7 @@ Look at the app now. The dashboard, heroes, and navigation links are styled.
<figure class='image-display'>
<img src='assets/images/devguide/toh/dashboard-top-heroes.png' alt="View navigations"></img>
<img src='assets/images/devguide/toh/heroes-dashboard-1.png' alt="View navigations"></img>
</figure>

View File

@ -503,8 +503,9 @@ The calling component can easily consume a single result in the form of a Promis
But requests aren't always done only once.
You may start one request,
cancel it, and make a different request before the server has responded to the first request.
A *request-cancel-new-request* sequence is difficult to implement with *Promises*, but
easy with *Observables*.
A *request-cancel-new-request* sequence is difficult to implement with `Promise`s, but
easy with `Observable`s.
### Add the ability to search by name
You're going to add a *hero search* feature to the Tour of Heroes.

View File

@ -3,6 +3,8 @@ var stylesRegex = /styleUrls *:(\s*\[[^\]]*?\])/g;
var stringRegex = /(['`"])((?:[^\\]\\\1|.)*?)\1/g;
module.exports.translate = function(load){
if (load.source.indexOf('moduleId') != -1) return load;
var url = document.createElement('a');
url.href = load.address;
@ -15,11 +17,13 @@ module.exports.translate = function(load){
baseHref.href = this.baseURL;
baseHref = baseHref.pathname;
basePath = basePath.replace(baseHref, '');
if (!baseHref.startsWith('/base/')) { // it is not karma
basePath = basePath.replace(baseHref, '');
}
load.source = load.source
.replace(templateUrlRegex, function(match, quote, url){
let resolvedUrl = url;
var resolvedUrl = url;
if (url.startsWith('.')) {
resolvedUrl = basePath + url.substr(1);