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

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

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

@ -9,7 +9,7 @@ module.exports = webpackMerge(commonConfig, {
output: {
path: helpers.root('dist'),
publicPath: 'http://localhost:8080/',
publicPath: '/',
filename: '[name].js',
chunkFilename: '[id].chunk.js'
},

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.
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 _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,7 +117,8 @@ 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.
@ -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,25 +157,29 @@ 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 we have defined for the job application is returned from the `QuestionService`.
In a real app we'd retrieve these questions from storage.
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 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.
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.
<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
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.
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, we are also adding validation dynamically.
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.
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.
@ -118,6 +130,9 @@ 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
@ -149,6 +164,10 @@ a message with *a variety of possible meanings* could have different translation
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.
@ -258,7 +277,7 @@ 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>
<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>.
@ -425,8 +444,8 @@ 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">
@ -447,6 +466,9 @@ 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
@ -504,13 +526,16 @@ Translating _plural_ and _select_ messages is a little tricky.
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.
{@a translate-plural}
### Translate _plural_
To translate a `plural`, translate its ICU format match values:
@ -521,6 +546,9 @@ To translate a `plural`, translate its ICU format match values:
{@a translate-select}
### Translate _select_
The `select` behaves a little differently. Here again is the ICU format message in the component template:
@ -618,14 +646,14 @@ 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.
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.
@ -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:
@ -707,19 +738,22 @@ and the corresponding translation file:
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.
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,7 +774,7 @@ more languages.
### _Internationalize_ with the AOT compiler
### _Internationalization_ with the AOT compiler
The JIT compiler translates the application into the target language
while compiling dynamically in the browser.
@ -750,7 +784,7 @@ 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
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.
@ -764,16 +798,16 @@ 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
@ -807,10 +841,10 @@ Windows users may have to quote the command:
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.
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.

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);