Merge remote-tracking branch 'origin/master'

# Conflicts:
#	public/docs/ts/latest/cookbook/_data.json
#	public/docs/ts/latest/guide/_data.json
#	public/docs/ts/latest/guide/attribute-directives.jade
#	public/docs/ts/latest/guide/component-styles.jade
#	public/docs/ts/latest/guide/hierarchical-dependency-injection.jade
#	public/docs/ts/latest/guide/style-guide.jade
#	public/docs/ts/latest/tutorial/toh-pt3.jade
#	public/docs/ts/latest/tutorial/toh-pt4.jade
#	public/docs/ts/latest/tutorial/toh-pt5.jade
This commit is contained in:
Zhicheng Wang 2016-05-21 21:02:15 +08:00
commit ae3b205c77
59 changed files with 705 additions and 460 deletions

View File

@ -86,21 +86,46 @@ var _exampleBoilerplateFiles = [
var _exampleDartWebBoilerPlateFiles = ['styles.css'];
// --filter may be passed in to filter/select _example app subdir names
// i.e. gulp run-e2e-tests --filter=foo ; would select all example apps with
// 'foo' in their folder names.
/**
* Run Protractor End-to-End Tests for Doc Samples
*
* Flags
* --filter to filter/select _example app subdir names
* e.g. gulp run-e2e-tests --filter=foo // all example apps with 'foo' in their folder names.
*
* --fast by-passes the npm install and webdriver update
* Use it for repeated test runs (but not the FIRST run)
* e.g. gulp run-e2e-tests --fast
*
* --lang to filter by code language
* e.g. gulp run-e2e-tests --lang=ts // only TypeScript apps
* default is (ts|js)
* all means (ts|js|dart)
*/
gulp.task('run-e2e-tests', function() {
var spawnInfo = spawnExt('npm', ['install'], { cwd: EXAMPLES_PATH});
return spawnInfo.promise.then(function() {
copyExampleBoilerplate();
var exePath = path.join(process.cwd(), "./node_modules/.bin/");
spawnInfo = spawnExt('webdriver-manager', ['update'], {cwd: exePath});
return spawnInfo.promise;
}).then(function() {
var exePath = path.join(process.cwd(), "./node_modules/.bin/");
var promise;
if (argv.fast) {
// fast; skip all setup
promise = Promise.resolve(true);
} else {
// Not 'fast'; do full setup
var spawnInfo = spawnExt('npm', ['install'], { cwd: EXAMPLES_PATH});
promise = spawnInfo.promise.then(function() {
copyExampleBoilerplate();
spawnInfo = spawnExt('webdriver-manager', ['update'], {cwd: exePath});
return spawnInfo.promise;
});
}
promise.then(function() {
return findAndRunE2eTests(argv.filter);
}).then(function(status) {
reportStatus(status);
}).fail(function(e) {
}).catch(function(e) {
gutil.log(e);
return e;
});
});
@ -114,10 +139,13 @@ function findAndRunE2eTests(filter) {
var startTime = new Date().getTime();
// create an output file with header.
var outputFile = path.join(process.cwd(), 'protractor-results.txt');
var header = "Protractor example results for " + lang + " on " + (new Date()).toLocaleString() + "\n\n";
if (filter) {
header += ' Filter: ' + filter.toString() + '\n\n';
}
var header = `Doc Sample Protractor Results for ${lang} on ${new Date().toLocaleString()}\n`;
header += argv.fast ?
' Fast Mode (--fast): no npm install, webdriver update, or boilerplate copy\n' :
' Slow Mode: npm install, webdriver update, and boilerplate copy\n';
header += ` Filter: ${filter ? filter : 'All tests'}\n\n`;
fs.writeFileSync(outputFile, header);
// create an array of combos where each
@ -175,25 +203,32 @@ function runE2eTsTests(appDir, protractorConfigFilename, outputFile) {
}
function runProtractor(prepPromise, appDir, appRunSpawnInfo, protractorConfigFilename, outputFile) {
return prepPromise.then(function (data) {
// start protractor
var pcFilename = path.resolve(protractorConfigFilename); // need to resolve because we are going to be running from a different dir
var exePath = path.join(process.cwd(), "./node_modules/.bin/");
var spawnInfo = spawnExt('protractor',
[ pcFilename, '--params.appDir=' + appDir, '--params.outputFile=' + outputFile], { cwd: exePath });
return spawnInfo.promise;
}).then(function(data) {
// kill the app now that protractor has completed.
// Ugh... proc.kill does not work properly on windows with child processes.
// appRun.proc.kill();
treeKill(appRunSpawnInfo.proc.pid);
return !data;
}).fail(function(err) {
// Ugh... proc.kill does not work properly on windows with child processes.
// appRun.proc.kill();
treeKill(appRunSpawnInfo.proc.pid);
return false;
});
return prepPromise
.catch(function(){
var emsg = `AppDir failed during compile: ${appDir}\n\n`;
gutil.log(emsg);
fs.appendFileSync(outputFile, emsg);
return Promise.reject(emsg);
})
.then(function (data) {
// start protractor
var pcFilename = path.resolve(protractorConfigFilename); // need to resolve because we are going to be running from a different dir
var exePath = path.join(process.cwd(), "./node_modules/.bin/");
var spawnInfo = spawnExt('protractor',
[ pcFilename, '--params.appDir=' + appDir, '--params.outputFile=' + outputFile], { cwd: exePath });
return spawnInfo.promise
})
.then(
function() { return finish(true);},
function() { return finish(false);}
)
function finish(ok){
// Ugh... proc.kill does not work properly on windows with child processes.
// appRun.proc.kill();
treeKill(appRunSpawnInfo.proc.pid);
return ok;
}
}
// start the server in appDir/build/web; then run protractor with the specified
@ -252,9 +287,11 @@ function spawnExt(command, args, options) {
proc.stderr.on('data', function (data) {
gutil.log(data.toString());
});
proc.on('close', function (data) {
proc.on('close', function (returnCode) {
gutil.log('completed: ' + descr);
deferred.resolve(data);
// Many tasks (e.g., tsc) complete but are actually errors;
// Confirm return code is zero.
returnCode === 0 ? deferred.resolve(0) : deferred.reject(returnCode);
});
proc.on('error', function (data) {
gutil.log('completed with error:' + descr);

View File

@ -2,7 +2,7 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
@Component({
selector:'countdown-timer',
selector: 'countdown-timer',
template: '<p>{{message}}</p>'
})
export class CountdownTimerComponent implements OnInit, OnDestroy {
@ -24,12 +24,12 @@ export class CountdownTimerComponent implements OnInit, OnDestroy {
private countDown() {
this.clearTimer();
this.intervalId = setInterval(()=>{
this.intervalId = window.setInterval(() => {
this.seconds -= 1;
if (this.seconds == 0) {
this.message = "Blast off!";
if (this.seconds === 0) {
this.message = 'Blast off!';
} else {
if (this.seconds < 0) { this.seconds = 10;} // reset
if (this.seconds < 0) { this.seconds = 10; } // reset
this.message = `T-${this.seconds} seconds and counting`;
}
}, 1000);

View File

@ -0,0 +1,32 @@
describe('Cookbook: component-relative paths', function () {
function getPageStruct() {
return {
title: element( by.tagName( 'h1' )),
absComp: element( by.css( 'absolute-path div' ) ),
relComp: element( by.css( 'relative-path div' ) )
}
}
var page;
beforeAll(function () {
browser.get('');
page = getPageStruct();
});
it('should display title of the sample', function () {
expect(element(by.tagName('h1')).getText()).toContain('Paths');
});
it('should have absolute-path element', function () {
expect(page.absComp.isPresent()).toBe(true, 'no <absolute-path> element');
});
it('should display the absolute path text', function () {
expect(page.absComp.getText()).toContain('Absolute');
});
it('should display the component-relative path text', function () {
expect(page.relComp.getText()).toContain('Component-relative');
});
});

View File

@ -0,0 +1,15 @@
// #docregion
import { Component } from '@angular/core';
import { SomeAbsoluteComponent, SomeRelativeComponent} from './some.component';
@Component({
selector: 'my-app',
template:
`<h1>Absolute & <i>Component-Relative</i> Paths</h1>
<absolute-path></absolute-path>
<relative-path></relative-path>
`,
directives: [SomeAbsoluteComponent, SomeRelativeComponent]
})
export class AppComponent {}

View File

@ -0,0 +1,5 @@
import { bootstrap } from '@angular/platform-browser-dynamic';
import { AppComponent } from './app.component';
bootstrap(AppComponent);

View File

@ -0,0 +1,22 @@
/* #docregion */
div.absolute {
background: beige;
border: 1px solid darkred;
color: red;
margin: 8px;
max-width: 20em;
padding: 4px;
text-align: center;
}
div.relative {
background: powderblue;
border: 1px solid darkblue;
color: Blue;
font-style: italic;
margin: 8px;
max-width: 20em;
padding: 4px;
text-align: center;
}

View File

@ -0,0 +1,4 @@
<!-- #docregion -->
<div class={{class}}>
{{type}}<br>{{path}}
</div>

View File

@ -0,0 +1,37 @@
// #docregion
import { Component } from '@angular/core';
///////// Using Absolute Paths ///////
// #docregion absolute-config
@Component({
selector: 'absolute-path',
templateUrl: 'app/some.component.html',
styleUrls: ['app/some.component.css']
})
// #enddocregion absolute-config
export class SomeAbsoluteComponent {
class = 'absolute';
type = 'Absolute template & style URLs';
path = 'app/path.component.html';
}
///////// Using Relative Paths ///////
// #docregion relative-config
@Component({
// #docregion module-id
moduleId: module.id,
// #enddocregion module-id
selector: 'relative-path',
templateUrl: 'some.component.html',
styleUrls: ['some.component.css']
})
// #enddocregion relative-config
export class SomeRelativeComponent {
class = 'relative';
type = 'Component-relative template & style URLs';
path = 'path.component.html';
}

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<base href="/">
<title>
Component-Relative Paths
</title>
<!-- #docregion style -->
<link rel="stylesheet" type="text/css" href="styles.css">
<!-- #enddocregion style -->
<!-- Polyfill(s) for older browsers -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('app').catch(function(err){ console.error(err); });
</script>
</head>
<body>
<my-app>Loading app...</my-app>
</body>
</html>

View File

@ -0,0 +1,8 @@
{
"description": "Module-relative Paths",
"files": [
"!**/*.d.ts",
"!**/*.js"
],
"tags": [ "cookbook" ]
}

View File

@ -8,7 +8,7 @@ import { LocationStrategy,
import { HeroData } from './hero-data';
import { InMemoryBackendService,
SEED_DATA } from 'angular2-in-memory-web-api/core';
SEED_DATA } from 'angular2-in-memory-web-api';
import { AppComponent } from './app.component';

View File

@ -1,90 +1,93 @@
/*global jasmine, __karma__, window*/
(function () {
// Error.stackTraceLimit = Infinity;
// /*global jasmine, __karma__, window*/
Error.stackTraceLimit = Infinity;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
// Cancel Karma's synchronous start,
// we call `__karma__.start()` later, once all the specs are loaded.
__karma__.loaded = function () { };
__karma__.loaded = function () {
};
// SET THE RUNTIME APPLICATION ROOT HERE
var appRoot ='app'; // no trailing slash!
// RegExp for client application base path within karma (which always starts 'base\')
var karmaBase = '^\/base\/'; // RegEx string for base of karma folders
var appPackage = 'base/' + appRoot; //e.g., base/app
var appRootRe = new RegExp(karmaBase + appRoot + '\/');
var onlyAppFilesRe = new RegExp(karmaBase + appRoot + '\/(?!.*\.spec\.js$)([a-z0-9-_\.\/]+)\.js$');
var moduleNames = [];
// Configure systemjs packages to use the .js extension for imports from the app folder
var packages = {};
packages[appPackage] = {
defaultExtension: false,
format: 'register',
map: Object.keys(window.__karma__.files)
.filter(onlyAppFiles)
// Create local module name mapping to karma file path for app files
// with karma's fingerprint in query string, e.g.:
// './hero.service': '/base/app/hero.service.js?f4523daf879cfb7310ef6242682ccf10b2041b3e'
.reduce(function (pathsMapping, appPath) {
var moduleName = appPath.replace(appRootRe, './').replace(/\.js$/, '');
pathsMapping[moduleName] = appPath + '?' + window.__karma__.files[appPath];
return pathsMapping;
}, {})
}
System.config({ packages: packages });
// Configure Angular for the browser and
// with test versions of the platform providers
Promise.all([
System.import('angular2/testing'),
System.import('angular2/platform/testing/browser')
])
.then(function (results) {
var testing = results[0];
var browser = results[1];
testing.setBaseTestProviders(
browser.TEST_BROWSER_PLATFORM_PROVIDERS,
browser.TEST_BROWSER_APPLICATION_PROVIDERS);
// Load all spec files
// (e.g. 'base/app/hero.service.spec.js')
return Promise.all(
Object.keys(window.__karma__.files)
.filter(onlySpecFiles)
.map(function (moduleName) {
moduleNames.push(moduleName);
return System.import(moduleName);
}));
})
.then(success, fail);
////// Helpers //////
function onlyAppFiles(filePath) {
return onlyAppFilesRe.test(filePath);
function isJsFile(path) {
return path.slice(-3) == '.js';
}
function onlySpecFiles(filePath) {
return /\.spec\.js$/.test(filePath);
function isSpecFile(path) {
return /\.spec\.js$/.test(path);
}
function success () {
console.log(
'Spec files loaded:\n ' +
moduleNames.join('\n ') +
'\nStarting Jasmine testrunner');
__karma__.start();
function isBuiltFile(path) {
var builtPath = '/base/app/';
return isJsFile(path) && (path.substr(0, builtPath.length) == builtPath);
}
function fail(error) {
__karma__.error(error.stack || error);
var allSpecFiles = Object.keys(window.__karma__.files)
.filter(isSpecFile)
.filter(isBuiltFile);
//////////////////////////
// Load our SystemJS configuration.
// map tells the System loader where to look for things
var map = {
'app': 'app',
'@angular': 'node_modules/@angular',
'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api',
'rxjs': 'node_modules/rxjs'
};
// packages tells the System loader how to load when no filename and/or no extension
var packages = {
'app': { main: 'main.js', defaultExtension: 'js' },
'rxjs': { defaultExtension: 'js' },
'angular2-in-memory-web-api': { main: 'index.js', defaultExtension: 'js' },
};
var ngPackageNames = [
'common',
'compiler',
'core',
'http',
'platform-browser',
'platform-browser-dynamic',
'router',
'router-deprecated',
'upgrade',
];
// Add package entries for angular packages
ngPackageNames.forEach(function(pkgName) {
// Bundled (~40 requests): DOESN'T WORK IN KARMA OR WALLABY (YET?)
//packages['@angular/'+pkgName] = { main: pkgName + '.umd.js', defaultExtension: 'js' };
// Individual files (~300 requests):
packages['@angular/'+pkgName] = { main: 'index.js', defaultExtension: 'js' };
});
var config = {
baseURL: '/base',
map: map,
packages: packages
}
})();
System.config(config);
//////////////
Promise.all([
System.import('@angular/core/testing'),
System.import('@angular/platform-browser-dynamic/testing')
]).then(function (providers) {
var testing = providers[0];
var testingBrowser = providers[1];
testing.setBaseTestProviders(
testingBrowser.TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,
testingBrowser.TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS);
}).then(function() {
// Finally, load all spec files.
// This will run the tests directly.
return Promise.all(
allSpecFiles.map(function (moduleName) {
return System.import(moduleName);
}));
}).then(__karma__.start, __karma__.error);

View File

@ -21,34 +21,30 @@ module.exports = function(config) {
}
},
files: [
// Polyfills.
'node_modules/code-js/client/shim.min.js',
// System.js for module loading
'node_modules/systemjs/dist/system.src.js',
// Zone.js dependencies
// Note - do not include zone.js itself here, it is already
// included in angular2-polyfills
// Polyfills
'node_modules/core-js/client/shim.js',
// Reflect and Zone.js
'node_modules/reflect-metadata/Reflect.js',
'node_modules/zone.js/dist/zone.js',
'node_modules/zone.js/dist/jasmine-patch.js',
'node_modules/zone.js/dist/async-test.js',
'node_modules/zone.js/dist/fake-async-test.js',
{ pattern: 'node_modules/reflect-metadata/Reflect.js', included: true, watched: false },
{ pattern: 'https://code.angularjs.org/tools/system.js', included: true, watched: false },
// RxJs.
{ pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false },
{ pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false },
{pattern: 'karma-test-shim.js', included: true, watched: true},
{pattern: 'built/test/matchers.js', included: true, watched: true},
// Angular 2 itself and the testing library
{pattern: 'node_modules/@angular/**/*.js', included: false, watched: false},
{pattern: 'node_modules/@angular/**/*.js.map', included: false, watched: false},
// paths loaded via module imports
{pattern: 'built/**/*.js', included: false, watched: true},
'karma-test-shim.js',
{pattern: 'node_modules/@angular/**/*.js', included: false, watched: true},
{pattern: 'node_modules/@angular/**/*.js.map', included: false, watched: true},
// transpiled application & spec code paths to be loaded via module imports
// transpiled application & spec code paths loaded via module imports
{pattern: appBase + '**/*.js', included: false, watched: true},
// asset (HTML & CSS) paths loaded via Angular's component compiler

View File

@ -0,0 +1,4 @@
{
"name": "HTTP client (server communication)"
"docHref": "https://angular.io/docs/dart/latest/guide/server-communication.html"
}

View File

@ -1,13 +1,13 @@
// #docplaster
// #docregion
import "package:angular2/core.dart" show Component;
import 'package:angular2/core.dart';
import "toh/hero_list_component.dart" show HeroListComponent;
import "wiki/wiki_component.dart" show WikiComponent;
import "wiki/wiki_smart_component.dart" show WikiSmartComponent;
import 'toh/hero_list_component.dart';
import 'wiki/wiki_component.dart';
import 'wiki/wiki_smart_component.dart';
@Component(
selector: "my-app",
selector: 'my-app',
template: '''
<hero-list></hero-list>
<my-wiki></my-wiki>

View File

@ -1,4 +1,6 @@
// #docregion
int _toInt(id) => id is int ? id : int.parse(id);
class Hero {
final int id;
final String name;
@ -9,6 +11,4 @@ class Hero {
new Hero(_toInt(hero['id']), hero['name']);
Map toJson() => {'id': id, 'name': name};
static int _toInt(id) => id is int ? id : int.parse(id);
}

View File

@ -6,12 +6,12 @@ import 'dart:convert';
import 'hero.dart';
import 'package:angular2/core.dart';
import 'package:http/browser_client.dart';
import 'package:http/http.dart' show Response;
import 'package:http/http.dart';
@Injectable()
class HeroService {
// #docregion endpoint, http-get
final String _heroesUrl = 'app/heroes'; // URL to web API
static const _heroesUrl = 'app/heroes'; // URL to web API
// #enddocregion endpoint, http-get
final BrowserClient _http;
@ -35,7 +35,7 @@ class HeroService {
// #docregion addhero, addhero-sig
Future<Hero> addHero(String name) async {
// #enddocregion addhero-sig
// #enddocregion addhero-sig
try {
final response = await _http.post(_heroesUrl,
headers: {'Content-Type': 'application/json'},
@ -50,8 +50,8 @@ class HeroService {
// #docregion extract-data
dynamic _extractData(Response res) {
var body = JSON.decode(res.body);
// TODO: once fixed, https://github.com/adaojunior/http-in-memory-web-api/issues/1
// Drop the `?? body` term
// TODO: https://github.com/adaojunior/http-in-memory-web-api/issues/1
// Once #1 is fixed, drop the `?? body` term:
return body['data'] ?? body;
}
// #enddocregion extract-data
@ -69,6 +69,6 @@ class HeroService {
/*
// #docregion endpoint-json
private _heroesUrl = 'heroes.json'; // URL to JSON file
static const _heroesUrl = 'heroes.json'; // URL to JSON file
// #enddocregion endpoint-json
*/

View File

@ -23,4 +23,3 @@ transformers:
resolved_identifiers:
BrowserClient: 'package:http/browser_client.dart'
- dart_to_js_script_rewriter
# #enddocregion transformers

View File

@ -1,10 +1,10 @@
// #docplaster
// #docregion final
import 'package:angular2/core.dart' show Provider;
import 'package:angular2/core.dart';
// #docregion v1
import 'package:angular2/platform/browser.dart' show bootstrap;
import 'package:angular2/platform/browser.dart';
// #docregion http-providers
import 'package:http/browser_client.dart' show BrowserClient;
import 'package:http/browser_client.dart';
// #enddocregion http-providers
import 'package:server_communication/app_component.dart';

View File

@ -5,7 +5,7 @@ import { provide } from '@angular/core';
import { XHRBackend } from '@angular/http';
import { InMemoryBackendService,
SEED_DATA } from 'angular2-in-memory-web-api/core';
SEED_DATA } from 'angular2-in-memory-web-api';
import { HeroData } from './hero-data';
// The usual bootstrapping imports

View File

@ -17,7 +17,7 @@
var packages = {
'app': { main: 'main.js', defaultExtension: 'js' },
'rxjs': { defaultExtension: 'js' },
'angular2-in-memory-web-api': { defaultExtension: 'js' },
'angular2-in-memory-web-api': { main: 'index.js', defaultExtension: 'js' },
};
var ngPackageNames = [

View File

@ -22,7 +22,7 @@
var packages = {
'app': { main: 'main.ts', defaultExtension: 'ts' },
'rxjs': { defaultExtension: 'js' },
'angular2-in-memory-web-api': { defaultExtension: 'js' },
'angular2-in-memory-web-api': { main: 'index.js', defaultExtension: 'js' },
};
var ngPackageNames = [

View File

@ -8,8 +8,10 @@ import {
beforeEach, beforeEachProviders,
describe, ddescribe, xdescribe,
expect, it, iit, xit,
async, inject, ComponentFixture, TestComponentBuilder
} from '@angular/testing';
async, inject
} from '@angular/core/testing';
import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing';
import { Hero, HeroService, MockHeroService } from './mock-hero.service';
@ -18,7 +20,7 @@ import { Router, MockRouter,
RouterOutlet, MockRouterOutlet} from './mock-router';
describe('AppComponent', () => {
let fixture: ComponentFixture;
let fixture: ComponentFixture<AppComponent>;
let comp: AppComponent;
beforeEach(async(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {

View File

@ -3,10 +3,10 @@
import { Component } from '@angular/core';
// Can't test with ROUTER_DIRECTIVES yet
// import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from '@angular/router';
// import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from '@angular/router-deprecated';
import { RouteConfig, RouterLink,
RouterOutlet, ROUTER_PROVIDERS } from '@angular/router';
RouterOutlet, ROUTER_PROVIDERS } from '@angular/router-deprecated';
import { DashboardComponent } from './dashboard.component';
import { HeroesComponent } from './heroes.component';

View File

@ -19,12 +19,13 @@ import { DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';
import {
beforeEach, beforeEachProviders, withProviders,
beforeEach, beforeEachProviders,
describe, ddescribe, xdescribe,
expect, it, iit, xit,
async, inject, fakeAsync, tick,
ComponentFixture, TestComponentBuilder
} from '@angular/testing';
async, inject
} from '@angular/core/testing';
import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing';
import { provide } from '@angular/core';
import { ViewMetadata } from '@angular/core';
@ -90,7 +91,7 @@ xdescribe('async & inject testing errors', () => {
let itPromise = patchJasmineIt();
it('should fail with an error from a promise', async(() => {
return Promise.reject('baz')
return Promise.reject('baz');
}));
itPromise.then(

View File

@ -16,12 +16,14 @@ import { DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';
import {
beforeEach, beforeEachProviders, withProviders,
beforeEach, beforeEachProviders,
describe, ddescribe, xdescribe,
expect, it, iit, xit,
async, inject, fakeAsync, tick,
ComponentFixture, TestComponentBuilder
} from '@angular/testing';
async, inject,
fakeAsync, tick, withProviders
} from '@angular/core/testing';
import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing';
import { provide } from '@angular/core';
import { ViewMetadata } from '@angular/core';
@ -349,7 +351,7 @@ describe('test component builder', function() {
})), 10000); // Long timeout because this test makes an actual XHR.
describe('(lifecycle hooks w/ MyIfParentComp)', () => {
let fixture: ComponentFixture;
let fixture: ComponentFixture<MyIfParentComp>;
let parent: MyIfParentComp;
let child: MyIfChildComp;

View File

@ -8,8 +8,10 @@ import {
beforeEach, beforeEachProviders,
describe, ddescribe, xdescribe,
expect, it, iit, xit,
async, inject, TestComponentBuilder
} from '@angular/testing';
async, inject
} from '@angular/core/testing';
import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing';
import { Hero, HeroService, MockHeroService } from './mock-hero.service';
import { Router, MockRouter } from './mock-router';

View File

@ -2,7 +2,7 @@
// #docregion
import { Component, OnInit } from '@angular/core';
// #docregion import-router
import { Router } from '@angular/router';
import { Router } from '@angular/router-deprecated';
// #enddocregion import-router
import { Hero } from './hero';

View File

@ -5,7 +5,7 @@
import { Component, OnInit } from '@angular/core';
// #enddocregion import-oninit
// #docregion import-route-params
import {RouteParams} from '@angular/router';
import {RouteParams} from '@angular/router-deprecated';
// #enddocregion import-route-params
import { Hero } from './hero';

View File

@ -1,7 +1,7 @@
// #docplaster
// #docregion
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Router } from '@angular/router-deprecated';
import { Hero } from './hero';
import { HeroDetailComponent } from './hero-detail.component';
@ -47,4 +47,4 @@ export class HeroesComponent implements OnInit {
}
// #enddocregion heroes-component-renaming
// #enddocregion class
// #enddocregion
// #enddocregion

View File

@ -1,10 +1,12 @@
/* tslint:disable:no-unused-variable */
import {
beforeEach, beforeEachProviders, withProviders,
beforeEach, beforeEachProviders,
describe, ddescribe, xdescribe,
expect, it, iit, xit,
async, inject, TestComponentBuilder
} from '@angular/testing';
async, inject, withProviders
} from '@angular/core/testing';
import { TestComponentBuilder } from '@angular/compiler/testing';
import { provide } from '@angular/core';

View File

@ -1,10 +1,10 @@
export * from '@angular/router';
export * from '@angular/router-deprecated';
import { Directive, DynamicComponentLoader, ViewContainerRef,
Injectable, Optional, Input } from '@angular/core';
import { ComponentInstruction, Instruction,
Router, RouterOutlet} from '@angular/router';
Router, RouterOutlet} from '@angular/router-deprecated';
let _resolveToTrue = Promise.resolve(true);

View File

@ -4,7 +4,7 @@
import { provide } from '@angular/core';
import { XHRBackend } from '@angular/http';
import { InMemoryBackendService, SEED_DATA } from 'angular2-in-memory-web-api/core';
import { InMemoryBackendService, SEED_DATA } from 'angular2-in-memory-web-api';
import { InMemoryDataService } from './in-memory-data.service';
// The usual bootstrapping imports

View File

@ -1,27 +1,25 @@
// Configuration for the Wallaby Visual Studio Code testing extension
// https://marketplace.visualstudio.com/items?itemName=WallabyJs.wallaby-vscode
// Note: Wallaby is not open source and costs money
module.exports = function () {
return {
files: [
// System.js for module loading
{pattern: 'node_modules/systemjs/dist/system-polyfills.js', instrument: false},
{pattern: 'node_modules/systemjs/dist/system.js', instrument: false},
// Polyfills
{pattern: 'node_modules/code-js/client/shim.min.js', instrument: false},
{pattern: 'node_modules/angular2/bundles/angular2-polyfills.js', instrument: false},
{pattern: 'node_modules/core-js/client/shim.min.js', instrument: false},
// Zone.js dependencies
// Note - do not include zone.js itself or long-stack-trace-zone.js` here as
// they are included already in angular2-polyfills
// Reflect, Zone.js, and test shims
// Rx.js, Angular 2 itself, and the testing library not here because loaded by systemjs
{pattern: 'node_modules/reflect-metadata/Reflect.js', instrument: false},
{pattern: 'node_modules/zone.js/dist/zone.js', instrument: false},
{pattern: 'node_modules/zone.js/dist/jasmine-patch.js', instrument: false},
{pattern: 'node_modules/zone.js/dist/async-test.js', instrument: false},
{pattern: 'node_modules/zone.js/dist/fake-async-test.js', instrument: false},
// Rx.js, Angular 2 itself, and the testing library not here because loaded by systemjs
{pattern: 'app/**/*+(ts|html|css)', load: false},
{pattern: 'app/**/*.spec.ts', ignore: true}
],
@ -36,41 +34,24 @@ module.exports = function () {
testFramework: 'jasmine',
debug: true,
bootstrap: function (wallaby) {
wallaby.delayStart();
systemConfig();
System.config({
defaultJSExtensions: true,
packages: {
app: {
meta: {
'*': {
scriptLoad: true
}
}
}
},
paths: {
'npm:*': 'node_modules/*'
},
map: {
'angular2': 'npm:angular2',
'rxjs': 'npm:rxjs'
}
});
// Configure Angular for the browser and
// with test versions of the platform providers
Promise.all([
System.import('angular2/testing'),
System.import('angular2/platform/testing/browser')
System.import('@angular/core/testing'),
System.import('@angular/platform-browser-dynamic/testing')
])
.then(function (results) {
var testing = results[0];
var browser = results[1];
.then(function (providers) {
var testing = providers[0];
var testingBrowser = providers[1];
testing.setBaseTestProviders(
browser.TEST_BROWSER_PLATFORM_PROVIDERS,
browser.TEST_BROWSER_APPLICATION_PROVIDERS);
testingBrowser.TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,
testingBrowser.TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS);
// Load all spec files
return Promise.all(wallaby.tests.map(function (specFile) {
@ -85,8 +66,58 @@ module.exports = function () {
throw e;
}, 0);
});
},
debug: true
//////////////////////////
// SystemJS configuration.
function systemConfig() {
// map tells the System loader where to look for things
var map = {
'app': 'app',
'@angular': 'node_modules/@angular',
'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api',
'rxjs': 'node_modules/rxjs'
};
// packages tells the System loader how to load when no filename and/or no extension
var packages = {
'app': { main: 'main.js', defaultExtension: 'js' },
'rxjs': { defaultExtension: 'js' },
'angular2-in-memory-web-api': { main: 'index.js', defaultExtension: 'js' },
};
var ngPackageNames = [
'common',
'compiler',
'core',
'http',
'platform-browser',
'platform-browser-dynamic',
'router',
'router-deprecated',
'upgrade',
];
// Add package entries for angular packages
ngPackageNames.forEach(function(pkgName) {
// Bundled (~40 requests): DOESN'T WORK IN WALLABY OR KARMA (YET?)
// packages['@angular/'+pkgName] = { main: pkgName + '.umd.js', defaultExtension: 'js' };
// Individual files (~300 requests):
packages['@angular/'+pkgName] = { main: 'index.js', defaultExtension: 'js' };
});
var config = {
map: map,
packages: packages
}
System.config(config);
}
//////////////////
}
};
};

View File

@ -14,12 +14,16 @@ include ../../../_includes/_util-fns
- var _liveLink = 'sample repo';
mixin liveExampleLink(linkText, exampleUrlPartName)
a(href='https://angular-examples.github.io/#{exampleUrlPartName}' target="_blank")= linkText
- var text = linkText || 'live example';
- var ex = exampleUrlPartName || getExampleName();
- var href = 'http://angular-examples.github.io/' + ex;
a(href='#{href}' target="_blank")= text
mixin liveExampleLink2(linkText, exampleUrlPartName)
- var liveExampleSourceLinkText = attributes.srcLinkText || 'view source'
| #[+liveExampleLink(linkText, exampleUrlPartName)]
| (#[a(href='https://github.com/angular-examples/#{exampleUrlPartName}' target="_blank") #{liveExampleSourceLinkText}])
- var srcText = attributes.srcText || 'view source';
- var ex = exampleUrlPartName || attributes.example || getExampleName();
- var href = 'http://github.com/angular-examples/' + ex;
span #[+liveExampleLink(linkText, ex)] (#[a(href='#{href}' target="_blank") #{srcText}])
- var adjustExamplePath = function(_path) {
- if(!_path) return _path;

View File

@ -17,6 +17,12 @@
"intro": "Share information between different directives and components"
},
"component-relative-paths": {
"title": "Component-relative Paths",
"intro": "Use relative URLs for component templates and styles.",
"hide": true
},
"dependency-injection": {
"title": "Dependency Injection",
"intro": "Techniques for Dependency Injection",

View File

@ -0,0 +1 @@
!= partial("../../../_includes/_ts-temp")

View File

@ -80,8 +80,8 @@
},
"server-communication": {
"title": "Http Client",
"intro": "Talk to a remote server with the Angular Http Client."
"title": "HTTP Client",
"intro": "Talk to a remote server with an HTTP Client."
},
"lifecycle-hooks": {

View File

@ -5,11 +5,11 @@ block includes
- var _Http = 'BrowserClient';
- var _Angular_Http = 'Dart <code>BrowserClient</code>'
- var _httpUrl = 'https://pub.dartlang.org/packages/http'
- var _Angular_http_library = 'Dart <a href="!{_httpUrl}"><b>http</b> library</a>'
- var _Angular_http_library = 'Dart <a href="' + _httpUrl + '"><b>http</b></a> library'
block demos-list
li HTTP client: Tour of Heroes
li JSONP client: Wikipedia to fetch data from a service that doesn't support CORS (under development)
li #[a(href="#http-client") HTTP client: Tour of Heroes]
li #[a(href="#cors") JSONP client: Wikipedia to fetch data from a service that does not support CORS] #[b (under development)]
block rxjs-import
//- N/A

View File

@ -16,6 +16,11 @@
"intro": "Share information between different directives and components"
},
"component-relative-paths": {
"title": "Component-relative Paths",
"intro": "Use relative URLs for component templates and styles."
},
"dependency-injection": {
"title": "Dependency Injection",
"intro": "Techniques for Dependency Injection"

View File

@ -0,0 +1 @@
!= partial("../../../_includes/_ts-temp")

View File

@ -80,8 +80,8 @@
},
"server-communication": {
"title": "Http Client",
"intro": "Talk to a remote server with the Angular Http Client."
"title": "HTTP Client",
"intro": "Talk to a remote server with an HTTP Client."
},
"lifecycle-hooks": {

View File

@ -5,8 +5,11 @@ include ../../../_includes/_util-fns
//- Other values match the defaults.
mixin liveExampleLink(linkText, exampleUrlPartName)
a(href='/resources/live-examples/#{exampleUrlPartName}/ts/plnkr.html' target="_blank")= linkText
- var text = linkText || 'live example';
- var ex = exampleUrlPartName || getExampleName();
- var href = '/resources/live-examples/' + ex + '/ts/plnkr.html';
a(href='#{href}' target="_blank")= text
mixin liveExampleLink2(linkText, exampleUrlPartName)
//- In Dart this also gives a link to the source.
| #[+liveExampleLink(linkText, exampleUrlPartName)]
//- In Dart this gives 2 links: to the demo and to the source.
+liveExampleLink(linkText, exampleUrlPartName)

View File

@ -15,6 +15,12 @@
"title": "组件通讯",
"intro": "在不同的指令和组件之间共享信息"
},
"component-relative-paths": {
"title": "相对于组件的路径",
"intro": "为组件的模板和样式指定相对于组件的路径"
},
"dependency-injection": {
"title": "依赖注入",
"intro": "依赖注入技术"

View File

@ -0,0 +1,140 @@
include ../_util-fns
:marked
## Write *Component-Relative* URLs to component templates and style files
Our components ofter refer to external template and style files.
We identify those files with a URL in the `templateUrl` and `styleUrls` properties of the `@Component` metadata
as seen here:
+makeExample('cb-component-relative-paths/ts/app/some.component.ts','absolute-config')(format='.')
:marked
By default, we *must* specify the full path back to the application root.
We call this an ***absolute path*** because it is *absolute* with respect to the application root.
There are two problems with an *absolute path*
1. We have to remember the full path back to the application root.
1. We have to update the URL when we move the component around in the application files structure.
It would be much easier to write and maintain our application components if we could specify template and style locations
*relative* to their component class file.
*We can!*
.alert.is-important
:marked
We can if we build our application as `commonjs` modules and load those modules
with a suitable package loader such as `systemjs` or `webpack`.
Learn why [below](#why-default).
The Angular 2 CLI uses these technologies and defaults to the
*component-relative path* approach described here.
CLI users can skip this chapter or read on to understand
how it works.
.l-main-section
:marked
## _Component-Relative_ Paths
Our goal is to specify template and style URLs *relative* to their component class files,
hence the term ***component-relative path***.
The key to success is following a convention that puts related component files in well-known locations.
We recommend keeping component template and component-specific style files as *siblings* of their
companion component class files.
Here we see the three files for `SomeComponent` sitting next to each other in the `app` folder.
.filetree
.file app
.children
.file some.component.css
.file some.component.html
.file some.component.ts
.file ...
:marked
We'll have more files and folders &mdash; and greater folder depth &mdash; as our application grows.
We'll be fine as long as the component files travel together as the inseparable siblings they are.
### Set the *moduleId*
Having adopted this file structure convention, we can specify locations of the template and style files
relative to the component class file simply by setting the `moduleId` property of the `@Component` metadata like this
+makeExample('cb-component-relative-paths/ts/app/some.component.ts','module-id')(format='.')
:marked
We strip the `app/` base path from the `templateUrl` and `styleUrls`. The result looks like this:
+makeExample('cb-component-relative-paths/ts/app/some.component.ts','relative-config')(format='.')
.alert.is-helpful
:marked
Webpack users may prefer [an alternative approach](#webpack) that uses `require`.
.l-main-section
:marked
## Source
**We can see the [live example](/resources/live-examples/cb-component-relative-paths/ts/plnkr.html)**
and download the source code from there
or simply read the pertinent source here.
+makeTabs(
`cb-component-relative-paths/ts/app/some.component.ts,
cb-component-relative-paths/ts/app/some.component.html,
cb-component-relative-paths/ts/app/some.component.css,
cb-component-relative-paths/ts/app/app.component.ts`,
null,
`app/some.component.ts, app/some.component.html, app/some.component.css, app/app.component.ts`)
a#why-default
.l-main-section
:marked
## Appendix: why *component-relative* is not the default
A *component-relative* path is obviously superior to an *absolute* path.
Why did Angular default to the *absolute* path?
Why do *we* have to set the `moduleId`? Why can't Angular set it?
First, let's look at what happens if we use a relative path and omit the `moduleId`.
`EXCEPTION: Failed to load some.component.html`
Angular can't find the file so it throws an error.
Why can't Angular calculate the template and style URLs from the component file's location?
Because the location of the component can't be determined without the developer's help.
Angular apps can be loaded in many ways: from individual files, from SystemJS packages, or
from CommonJS packages, to name a few.
We might generate modules in any of several formats.
We might not be writing modular code at all!
With this diversity of packaging and module load strategies,
it's not possible for Angular to know with certainty where these files reside at runtime.
The only location Angular can be sure of is the URL of the `index.html` home page, the application root.
So by default it resolves template and style paths relative to the URL of `index.html`.
That's why we previously wrote our file URLs with an `app/` base path prefix.
But *if* we follow the recommended guidelines and we write modules in `commonjs` format
and we use a module loader that *plays nice*,
*then* we &mdash; the developers of the application &mdash;
know that the semi-global `module.id` variable is available and contains
the absolute URL of the component class module file.
That knowledge enables us to tell Angular where the *component* file is
by setting the `moduleId`:
+makeExample('cb-component-relative-paths/ts/app/some.component.ts','module-id')(format='.')
a#webpack
.l-main-section
:marked
## Webpack: load templates and styles with *require*
Webpack developers have an alternative to `moduleId`.
They can load templates and styles at runtime by setting the component metadata `template` and `style` properties
with `require` statements that reference *component-relative* URLS.
+makeExample('webpack/ts/src/app/app.component.ts')(format='.')
:marked
See the [Introduction to Webpack](../guide/webpack.html).

View File

@ -80,8 +80,8 @@
},
"server-communication": {
"title": "Http客户端",
"intro": "通过Angular Http客户端与远程服务器对话。"
"title": "HTTP客户端",
"intro": "通过HTTP客户端与远程服务器对话。"
},
"lifecycle-hooks": {

View File

@ -19,8 +19,8 @@ block includes
* [pass values into the directive using data binding](#bindings)
* [使用数据绑定把值传到指令中](#bindings)
p.
#[+liveExampleLink2('在线例子', 'attribute-directives')].
p Try the #[+liveExampleLink2()].
:marked
## Directives overview
## 指令概览

View File

@ -32,12 +32,9 @@ block includes
* [Appendix 1: Inspecting the generated runtime component styles](#inspect-generated-css)
* [附录1: 审查生成的运行时组件样式](#inspect-generated-css)
* [Appendix 2: Loading Styles with Relative URLs](#relative-urls)
* [附录2: 用相对URL加载样式](#relative-urls)
p
| #[+liveExampleLink2('Run the live example', 'component-styles')]&nbsp;
| of the code shown in this chapter.
p
| #[+liveExampleLink2('运行本章代码的在线范例', 'component-styles')]
p Run the #[+liveExampleLink2()] of the code shown in this chapter.
p 运行本章这些代码的#[+liveExampleLink2()]
.l-main-section
:marked
@ -473,96 +470,12 @@ code-example(format='').
block module-id
:marked
We'd *prefer* to write this:
We can change the way Angular calculates the full URL be setting the component metadata's `moduleId` property to `module.id`.
我们期望的*首选*方式是这样写:
+makeExample('component-styles/ts/app/quest-summary.component.ts', 'urls')(format='.')
:marked
We can't do that by default. Angular can't find the files and throws an error:
但默认情况下我们不能这么做。Angular找不到这个文件并且抛出一个错误
`EXCEPTION: Failed to load quest-summary.component.html`
`异常不能加载quest-summary.component.html文件EXCEPTION: Failed to load quest-summary.component.html`
Why can't Angular calculate the HTML and CSS URLs from the component file's location?
为什么Angular不能根据组件文件的位置计算出HTML和CSS文件的URL呢
Unfortunately, that location is not readily known.
Angular apps can be loaded in many ways: from individual files, from SystemJS packages, or
from CommonJS packages, to name a few.
With this diversity of load strategies, it's not easy to tell at runtime where these files actually reside.
很不幸,当前的位置并不是已知的。
Angular应用可以用多种方式被加载从独立的文件、从SystemJS包或从CommonJS包等等。
由于存在这么多种加载策略,要想在运行期知道这些文件的实际位置,可不是一件容易的事。
The only location Angular can be sure of is the URL of the `index.html` home page.
So by default it resolves template and style paths relative to the URL of `index.html`.
That's why we previously wrote our CSS file URLs with an `app/` base path prefix.
Angular唯一能够确定的位置就是起始页`index.html`的URL。
所以,默认情况下,它都是以`index.html`的URL为基准来计算模板和样式的相对路径的。
这就是为什么我们以前写自身CSS文件的URL都以`app/`基本路径作为前缀。
Although this works with any code loading scheme, it is very inconvenient.
We move file folders around all the time during the evolution of our applications.
It's no fun patching the style and template URLs when we do.
虽然这能够和各种代码加载方案协同工作,但它很不方便。
在应用程序的进化过程中我们经常要移动文件夹。整天被迫修改样式和模板的URL可一点儿都不好玩儿
### *moduleId*
### *moduleId*
We can change the way Angular calculates the full URL be setting the component metadata's `moduleId` property.
我们可以通过设置组件元数据中的`moduleId`属性我们可以改变Angular计算完整URL的方式。
If we knew the component file's base path, we'd set `moduleId` to that and
let Angular construct the full URL from this base path plus the CSS and template file names.
如果我们知道组件文件的基本路径,我们就可以把`moduleId`设置成它,
让Angular可以用这个基本路径加上CSS和模板文件的名字来构造出完整URL。
Our challenge is to calculate the base path with minimal effort.
If it's too hard, we shouldn't bother; we should just write the full path to the root and move on.
Fortunately, *certain* module loaders make it relatively easy to find the base path.
我们的挑战是如何用最小的代价计算出这个基本路径。
如果太难,我们也不想自找麻烦,还不如直接写到程序根目录的全路径,然后继续。
幸运的是,在*某些*模块加载器中找到基本路径要相对简单一些。
SystemJS (starting in v.0.19.19) sets a *semi-global* variable to the URL of the component file.
That makes it trivial to set the component metadata `moduleId` property to the component's URL
and let Angular determine the module-relative paths for style and template URLs from there.
SystemJS(自从v.0.19.19起)设置了一个*半全局*变量来指向组件文件的URL。
有了它,把组件元数据的`moduleId`属性设置为组件本身的URL就简单多了
这样Angular就能让样式和模板的URL使用相对于模块的路径。
The name of the *semi-global* variable depends upon whether we told TypeScript to transpile to
'system' or 'commonjs' format (see the `module` option in the
[TypeScript compiler documentation](http://www.typescriptlang.org/docs/handbook/compiler-options.html)).
The variables are `__moduleName` and `module.id` respectively.
这个*半全局*变量的名字取决于我们是告诉TypeScript转译成'system'还是'commonjs'格式
(参见[TypeScript编译器文档](http://www.typescriptlang.org/docs/handbook/compiler-options.html)中的`module`选项)。
这个变量分别是`__moduleName`system格式和`module.id`commonjs格式
Here's an example in which we set the metadata `moduleId` to `module.id`.
下面的例子中示范了我们如何把元数据中的`moduleId`设置为`module.id`。
通过把组件元数据的`moduleId`属性设置为`module.id`我们可以更改Angular计算完整URL的方式
+makeExample('component-styles/ts/app/quest-summary.component.ts','', 'app/quest-summary.component.ts')
.l-sub-section
:marked
With a module bundler like Webpack we are more likely to set the `styles` and `template` properties with the bundler's
`require` mechanism rather than bother with `styleUrls` and `templateUrl`.
如果使用像Webpack这样的模块打包器我们更喜欢借助打包器本身的`require`机制来设置`styles`和`template`属性,而不是`styleUrls`和`templateUrl`。
:marked
Learn more about `moduleId` in the [Component-Relative Paths](../cookbook/component-relative-paths.html) chapter.
要学习更多关于`moduleId`的知识,请参见[相对于组件的路径](../cookbook/component-relative-paths.html)一章。

View File

@ -20,8 +20,9 @@ block includes
In this chapter we explore these points and write some code.
在本章中,我们将浏览这些要点,并写点代码儿来验证它。
p
| Try the #[+liveExampleLink2('live example', 'hierarchical-dependency-injection')].
p Try the #[+liveExampleLink2()].
p 试试#[+liveExampleLink2()].
.l-main-section
:marked

View File

@ -24,7 +24,7 @@ block includes
* [AfterViewInit and AfterViewChecked](#afterview)
* [AfterContentInit and AfterContentChecked](#aftercontent)
p Try the #[+liveExampleLink2('live example', 'lifecycle-hooks')].
p Try the #[+liveExampleLink2()].
a#hooks-overview
.l-main-section
@ -199,7 +199,7 @@ a#the-sample
.l-main-section
h2 Lifecycle exercises
p.
The #[+liveExampleLink('live example', 'lifecycle-hooks')]
The #[+liveExampleLink()]
demonstrates the lifecycle hooks in action through a series of exercises
presented as components under the control of the root `AppComponent`.
:marked

View File

@ -17,7 +17,7 @@ block includes
In fact, we'd like to apply them in our HTML templates as we do styles.
p.
Welcome, Angular pipes, the simple display-value transformations that we can declare in our HTML!
Try the #[+liveExampleLink2('live example', 'pipes')].
Try the #[+liveExampleLink2()].
.l-main-section
:marked
@ -169,7 +169,7 @@ figure.image-display
Custom pipes must be registered manually.
p.
If we try the #[+liveExampleLink('live code', 'pipes')] example,
If we try the #[+liveExampleLink()],
we can probe its behavior by changing the value and the optional exponent in the template.
:marked
@ -219,10 +219,10 @@ a#change-detection
Here's the `FlyingHeroesPipe` implementation which follows the pattern for custom pipes we saw earlier.
+makeExample('pipes/ts/app/flying-heroes.pipe.ts', 'pure', 'app/flying-heroes.pipe.ts')(format='.')
:marked
When we run the sample now we see odd behavior (try it in the [live example](/resources/live-examples/pipes/ts/plnkr.html)).
p.
When we run the sample now we see odd behavior (try it in the #[+liveExampleLink()]).
Every hero we add is a flying hero but none of them are displayed.
:marked
Although we're not getting the behavior we want, Angular isn't broken.
It's just using a different change detection algorithm &mdash; one that ignores changes to the list or any of its items.
@ -238,7 +238,7 @@ a#change-detection
*If we **mutate** the #{_array}, no pipe is invoked and no display updated;
if we **replace** the #{_array}, then the pipe executes and the display is updated*.
The *Flying Heroes* in the [live example](/resources/live-examples/pipes/ts/plnkr.html) extends the
The *Flying Heroes* extends the
code with checkbox switches and additional displays to help us experience these effects.
figure.image-display
@ -328,15 +328,15 @@ block pure-change
We can derive a `FlyingHeroesImpureComponent` that we derive from the `FlyingHeroesComponent`.
+makeExample('pipes/ts/app/flying-heroes.component.ts','impure-component','app/flying-heroes.component.ts (FlyingHeroesImpureComponent)')(format='.')
:marked
p.
The only substantive change is the pipe.
We can confirm in the [live example](/resources/live-examples/pipes/ts/plnkr.html)
that the *flying heroes* display updates as we enter new heroes even when we mutate the `heroes` #{_array}.
- var _dollar = _docsFor === 'ts' ? '$' : '';
:marked
<a id="async-pipe"></a>
### The impure *AsyncPipe*
We can confirm in the #[+liveExampleLink()] that the #[i flying heroes]
display updates as we enter new heroes even when we mutate the
#[code heroes] #{_array}.
- var _dollar = _docsFor === 'ts' ? '$' : '';
h3#async-pipe The impure #[i AsyncPipe]
:marked
The Angular `AsyncPipe` is an interesting example of an impure pipe.
The `AsyncPipe` accepts a `#{_Promise}` or `#{_Observable}` as input
and subscribes to the input automatically, eventually returning the emitted value(s).

View File

@ -16,7 +16,7 @@ block includes
[JSONP](https://en.wikipedia.org/wiki/JSONP). A few browsers also support
[Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).
The Angular HTTP client library simplifies application programming of the **XHR** and **JSONP** APIs
The !{_Angular_http_library} simplifies application programming of the **XHR** and **JSONP** APIs
as we'll learn in this chapter covering:
ul
@ -30,25 +30,25 @@ ul
li #[a(href="#update") Send data to the server]
+ifDocsFor('ts')
li #[a(href="#promises") Promises instead of observables]
li #[a(href="#cross-origin-requests") Cross-origin requests: Wikipedia example]
li #[a(href="#cors") Cross-origin requests: Wikipedia example]
+ifDocsFor('ts')
ul
li #[a(href="#search-parameters") Set query string parameters]
li #[a(href="#more-observables") Debounce search term input]
li #[a(href="#in-mem-web-api") Appendix: the in-memory web api service]
p.
p.
We illustrate these topics with code that you can
#[+liveExampleLink2('run live in a browser', 'server-communication')].
#[+liveExampleLink2('run live in a browser')].
.l-main-section
h1 Demos
p This chapter describes server communication with the help of the following demos
ul
block demos-list
li HTTP client: Tour of Heroes with Observables
li HTTP client: Tour of Heroes with #{_Promise}s
li JSONP client: Wikipedia to fetch data from a service that doesn't support CORS
li JSONP client: Wikipedia using observable operators to reduce server calls
li #[a(href="#http-client") HTTP client: Tour of Heroes with Observables]
li #[a(href="#promises") HTTP client: Tour of Heroes with #{_Promise}s]
li #[a(href="#cors") JSONP client: Wikipedia to fetch data from a service that does not support CORS]
li #[a(href="#more-observables") JSONP client: Wikipedia using observable operators to reduce server calls]
:marked
These demos are orchestrated by the root `AppComponent`
+makeExample('server-communication/ts/app/app.component.ts', null, 'app/app.component.ts')
@ -58,7 +58,7 @@ block rxjs-import
There is nothing remarkable here _except_ for the import of RxJS operators.
+makeExample('server-communication/ts/app/app.component.ts', 'import-rxjs')(format='.')
:marked
We'll talk about that [below](#rxjs) when we're ready to explore observables.
We'll talk about that [below](#rxjs) when we're ready to explore observables.
:marked
First, we have to configure our application to use server communication facilities.
@ -66,14 +66,14 @@ block rxjs-import
h1#http-providers Providing HTTP Services
:marked
We use the !{_Angular_Http} client to communicate with a server using a familiar HTTP request/response protocol.
The `!{_Http}` client is one of a family of services in the !{_Angular_http_library}.
The `#{_Http}` client is one of a family of services in the !{_Angular_http_library}.
block system-config-of-http
.l-sub-section
:marked
SystemJS knows how to load services from the !{_Angular_http_library} when we import from the `@angular/http` module
because we registered that module name in the `system.config` file.
:marked
Before we can use the `!{_Http}` client , we'll have to register it as a service provider with the Dependency Injection system.
Before we can use the `#{_Http}` client , we'll have to register it as a service provider with the Dependency Injection system.
.l-sub-section
:marked
Learn about providers in the [Dependency Injection](dependency-injection.html) chapter.
@ -84,7 +84,7 @@ p In this demo, we register providers in the #[code bootstrap] method of #[code
block http-providers
:marked
We begin by importing the symbols we need, most of them familiar by now. The newcomer is `HTTP_PROVIDERS`,
a collection of service providers from the Angular HTTP library.
a collection of service providers from the !{_Angular_http_library}.
We register HTTP providers in the bootstrap method by passing them in an array as the second parameter after the root component.
@ -105,10 +105,8 @@ block http-providers
For this reason, and this reason *only*, we hide it *above* the `AppComponent` in `main.ts`.
.l-main-section
a#http-client
h1#http-client The Tour of Heroes #[i HTTP] Client Demo
:marked
# The Tour of Heroes *HTTP* Client Demo
Our first demo is a mini-version of the [tutorial](../tutorial)'s "Tour of Heroes" (ToH) application.
This version gets some heroes from the server, displays them in a list, lets us add new heroes, and saves them to the server.
We use the !{_Angular_Http} client to communicate via `XMLHttpRequest (XHR)`.
@ -182,7 +180,7 @@ h2#fetch-data Fetch data with the #[b HeroService]
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'v1', 'app/toh/hero.service.ts (revised)')
:marked
Notice that the Angular `!{_Http}` client service is
Notice that the !{_Angular_Http} client service is
[injected](dependency-injection.html) into the `HeroService` constructor.
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'ctor')
:marked
@ -237,7 +235,7 @@ block rxjs
because the full library is so big. We only use a few operators in our app.
Instead, we'll import each `Observable` operator and static class method, one-by-one, until we have a custom *Observable* implementation tuned
precisely to our requirements. We'll put the `import` statements in one `app/add-rxjs-operators.ts` file.
precisely to our requirements. We'll put the `import` statements in one `app/rxjs-operators.ts` file.
+makeExample('server-communication/ts/app/rxjs-operators.ts', null, 'app/rxjs-operators.ts')(format=".")
:marked
If we forget an operator, the TypeScript compiler will warn that it's missing and we'll update this file.
@ -412,10 +410,8 @@ block hero-list-comp-add-hero
+makeExample('server-communication/ts/app/toh/hero-list.component.ts', 'addHero', 'app/toh/hero-list.component.ts (addHero)')(format=".")
block promises
a#promises
h2#promises Fall back to Promises
:marked
## Fall back to Promises
Although the Angular `http` client API returns an `Observable<Response>` we can turn it into a
[Promise<Response>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) if we prefer.
It's easy to do and a promise-based version looks much like the observable-based version in simple cases.
@ -461,11 +457,9 @@ block promises
Learn more about observables to understand the implications and consequences of subscriptions.
a#cross-origin-requests
h2#cors Cross-origin requests: Wikipedia example
:marked
## Cross-origin requests: Wikipedia example
We just learned how to make `XMLHttpRequests` using Angular's built-in `Http` service.
We just learned how to make `XMLHttpRequests` using the !{_Angular_Http} service.
This is the most common approach for server communication.
It doesn't work in all scenarios.
@ -496,7 +490,7 @@ figure.image-display
block wikipedia-jsonp+
:marked
Wikipedia offers a modern `CORS` API and a legacy `JSONP` search API. Let's use the latter for this example.
The Angular `Jsonp` service both extends the `Http` service for JSONP and restricts us to `GET` requests.
The Angular `Jsonp` service both extends the `#{_Http}` service for JSONP and restricts us to `GET` requests.
All other HTTP methods throw an error because JSONP is a read-only facility.
As always, we wrap our interaction with an Angular data access client service inside a dedicated service, here called `WikipediaService`.
@ -638,7 +632,7 @@ block wikipedia-jsonp+
.l-sub-section
:marked
We added the `debounceTime`, `distinctUntilChanged`, and `switchMap` operators to the RxJS `Observable` class
in `add-rxjs-operators` as [described above](#rxjs)
in `rxjs-operators` as [described above](#rxjs)
a#in-mem-web-api
.l-main-section
@ -694,4 +688,4 @@ block redirect-to-web-api
p Here is the revised (and final) version of the #[code #[+adjExPath('app/main.ts')]] demonstrating these steps.
+makeExample('server-communication/ts/app/main.ts', 'final', 'app/main.ts (final)')(format=".")
p See the full source code in the #[+liveExampleLink2('live example', 'server-communication')].
p See the full source code in the #[+liveExampleLink2()].

View File

@ -14,8 +14,8 @@ block includes
- [discover the &lt;template> element](#template)
- [understand the asterisk (\*) in **ngFor*](#asterisk)
- [write our own structural directive](#unless)
p
| Try the #[+liveExampleLink2('live example', 'structural-directives')].
p Try the #[+liveExampleLink2()].
<a id="definition"></a>
.l-main-section

View File

@ -496,9 +496,9 @@ a(href="#toc") 回到顶部
.s-why
:marked
**Why?** Clear service names such as `logger` do not require a suffix.
**Why?** Clear service names such as `Logger` do not require a suffix.
**为何?**清楚的服务名,比如`logger`不需要后缀。
**为何?**清楚的服务名,比如`Logger`不需要后缀。
.s-why.s-why-last
:marked
@ -537,7 +537,7 @@ table(width="100%")
td
code-example.
@Injectable()
export class LoggerService {}
export class Logger {}
td
:marked
logger.service.ts

View File

@ -33,7 +33,7 @@ block includes
h1 Try it!
h1 试试!
p
| Try the #[+liveExampleLink2('live example', 'quickstart')] which loads the sample app
| Try the #[+liveExampleLink2()] which loads the sample app
+ifDocsFor('ts')
| &nbsp;in #[a(href="http://plnkr.co/" title="Plunker" target="_blank") plunker]
| &nbsp;and displays the simple message:

View File

@ -268,7 +268,7 @@ code-example(format=".").
`AppComponent`的模板是这样的:
+makeExample('toh-3/ts/app/app.component.ts', 'hero-detail-template', 'app.component.ts (模板)')
+makeExample('toh-3/ts/app/app.component.ts', 'hero-detail-template', 'app.component.ts (Template)')(format='.')
:marked
Thanks to the binding, the `HeroDetailComponent` should receive the hero from the `AppComponent` and display that hero's detail beneath the list.
The detail should update every time the user picks a new hero.
@ -302,9 +302,7 @@ code-example(format=".").
我们要把它列在元数据的`directives`数组中这样Angular才会知道它。
让我们把这个数组属性加在`@Component`配置对象的底部,紧跟在`template`和`styles`属性之后。
+makeExample('toh-3/ts/app/app.component.ts', 'directives')
+makeExample('toh-3/ts/app/app.component.ts', 'directives', 'app/app.component.ts (Directives)')
:marked
### It works!
@ -340,7 +338,7 @@ code-example(format=".").
.file hero-detail.component.ts
.file main.ts
.file node_modules ...
.file typings ...
.file typings ...
.file index.html
.file package.json
.file tsconfig.json

View File

@ -270,7 +270,7 @@ code-example(format="." language="bash").
### Inject the *HeroService*
### 注入 *HeroService*
Two lines replace the one line of *new*:
Two lines replace the one line that created with *new*:
用这两行代码代替用`new`时的一行:
1. we add a constructor.
@ -292,15 +292,6 @@ code-example(format="." language="bash").
现在Angular将会知道当它创建`AppComponent`实例时,需要先提供一个`HeroService`的实例。
Angular has to get that instance from somewhere. That's the role of the Angular *Dependency Injector*.
The **Injector** has a **container** of previously created services.
Either it finds and returns a pre-existing `HeroService` from its container or it creates a new instance, adds
it to the container, and returns it to Angular.
Angular得想办法获得这个实例。这就是Angular *依赖注入器Dependency Injector* 扮演的角色。
这个 **注入器** 有一个包括以前创建过的所有服务的 **容器** 。
它既可以从容器中查找并返回一个已存在的`HeroService`实例又可以创建一个新的实例把它添加到容器中然后把它返回给Angular。
.l-sub-section
:marked
Learn more about Dependency Injection in the [Dependency Injection](../guide/dependency-injection.html) chapter.
@ -328,35 +319,7 @@ code-example(format="." language="html").
:marked
The `providers` array tells Angular to create a fresh instance of the `HeroService` when it creates a new `AppComponent`.
The `AppComponent` can use that service to get heroes and so can every child component of its component tree.
`providers`数组告诉Angular在创建一个新的`AppComponent`时,也要创建一个`HeroService`的新鲜实例。
`AppComponent`可以用此服务来获取英雄列表,它组件树中的每一个子组件也可以这么做。
<a id="child-component"></a>
.l-sub-section
:marked
### Services and the component tree
### 服务和组件树
Recall that the `AppComponent` creates an instance of `HeroDetail` by virtue of the
`<my-hero-detail>` tag at the bottom of its template. That `HeroDetail` is a child of the `AppComponent`.
回忆一下,`AppComponent`在它的模板底部包含了一个`<my-hero-detail>`标签,于是创建了一个`HeroDetail`的实例。这个`HeroDetail`就叫做`AppComponent`的子组件。
If the `HeroDetailComponent` needed its parent component's `HeroService`,
it would ask Angular to inject the service into its constructor which would look just like the one for `AppComponent`:
如果`HeroDetailComponent`需要访问来自它父组件的`HeroService`服务它可以要求Angular把这个服务注入到自己的构造函数中 —— 就像在`AppComponent`中的做法一样。
+makeExample('toh-4/ts/app/app.component.1.ts', 'ctor', 'hero-detail.component.ts (构造函数)')
:marked
The `HeroDetailComponent` must *not* repeat its parent's `providers` array! Guess [why](#shadow-provider).
`HeroDetailComponent`不能再写一遍它父组件的`providers`数组!你猜这是[为什么](#shadow-provider)。
The `AppComponent` is the top level component of our application.
There should be only one instance of that component and only one instance of the `HeroService` in our entire app.
`AppComponent`是我们应用的顶层组件。在我们的整个应用中,应该有唯一的一个顶层组件,应该有唯一的一个`HeroService`实例。
a#child-component
:marked
### *getHeroes* in the *AppComponent*
### *AppComponent* 中的 *getHeroes*
@ -629,30 +592,3 @@ code-example(format="." language="html").
Back in the `AppComponent`, replace `heroService.getHeroes` with `heroService.getHeroesSlowly`
and see how the app behaves.
回到`AppComponent`中,把`heroService.getHeroes`替换为`heroService.getHeroesSlowly`,看看应用的行为有什么变化。
.l-main-section
<a id="shadow-provider"></a>
:marked
### Appendix: Shadowing the parent's service
### 附件:遮蔽父组件的服务
We stated [earlier](#child-component) that if we injected the parent `AppComponent` `HeroService`
into the `HeroDetailComponent`, *we must not add a providers array* to the `HeroDetailComponent` metadata.
我们在[前面](#child-component)说过,如果在父组件`AppComponent`中把`HeroService`服务注入到`HeroDetailComponent`
*我们就不应该在`HeroDetailComponent`的元数据中再添加一个`providers`数组*。
Why? Because that tells Angular to create a new instance of the `HeroService` at the `HeroDetailComponent` level.
The `HeroDetailComponent` doesn't want its *own* service instance; it wants its *parent's* service instance.
Adding the `providers` array creates a new service instance that shadows the parent instance.
为什么呢因为那会告诉Angular在`HeroDetailComponent`这一层创建一个新的`HeroService`实例。
显然,在这个例子中,`HeroDetailComponent`不会希望创建*自己的*服务实例,它想要的就是来自父组件的服务实例。
给组件添加一个`providers`数组,就会创建一个新的服务实例,而它会遮蔽父组件中的同名实例。
Think carefully about where and when to register a provider.
Understand the scope of that registration. Be careful not to create a new service instance at the wrong level.
务必想清楚,在哪里、在什么时候注册供应商。要理解注册的有效范围。小心点!不要在错误的级别上创建新的服务实例。

View File

@ -466,9 +466,13 @@ code-example(format="." language="bash").
+makeExample('toh-5/ts/app/dashboard.component.ts', 'template-url', 'app/dashboard.component.ts (templateUrl)')(format=".")
.l-sub-section
:marked
We specify the path _all the way back to the application root_. Angular doesn't support module-relative paths.
We specify the path _all the way back to the application root_ &mdash; `app/` in this case &mdash;
because Angular doesn't support relative paths _by default_.
We _can_ switch to [component-relative paths](../cookbook/component-relative-paths) if we prefer.
我们指定的所有路径都是相对于该应用的根目录的。Angular不支持使用相对于当前模块的路径。
我们指定的所有路径_都是相对于该应用的根目录这里是`app/`的_。
因为Angular_默认_不支持使用相对于当前模块的路径。
只要喜欢我们也_可以_切换成[相对于组件的路径](../cookbook/component-relative-paths)模式。
:marked
Create that file with these contents: