diff --git a/.travis.yml b/.travis.yml index b0f7d92a6a..c6cadbafa5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,20 +10,24 @@ env: - DBUS_SESSION_BUS_ADDRESS=/dev/null - DISPLAY=:99.0 - CHROME_BIN=chromium-browser + - LATEST_RELEASE=2.0.0-rc.4 matrix: - - SCRIPT=lint - - SCRIPT="run-e2e-tests --fast" - - SCRIPT="run-e2e-tests --fast" PREVIEW=true + - TASK=lint + - TASK="run-e2e-tests --fast" SCRIPT=examples-install.sh + - TASK="run-e2e-tests --fast" SCRIPT=examples-install-preview.sh + - TASK=harp-compile SCRIPT=deploy-install.sh + - TASK=harp-compile SCRIPT=deploy-install-preview.sh matrix: fast_finish: true allow_failures: - - env: "SCRIPT=\"run-e2e-tests --fast\" PREVIEW=true" + - env: "TASK=\"run-e2e-tests --fast\" SCRIPT=examples-install-preview.sh" + - env: "TASK=harp-compile SCRIPT=deploy-install-preview.sh" before_install: - npm install -g gulp --no-optional +install: + - npm install --no-optional + - if [[ $SCRIPT ]]; then ./scripts/$SCRIPT; fi before_script: - sh -e /etc/init.d/xvfb start -install: - - ./scripts/install.sh - - if [[ $PREVIEW == true ]]; then npm install --prefix public/docs/_examples angular/{core,common,compiler,platform-browser,platform-browser-dynamic,http,forms,router-deprecated,router,upgrade}-builds; fi script: - - gulp $SCRIPT + - gulp $TASK diff --git a/gulpfile.js b/gulpfile.js index 5b5f093b56..f589774508 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -403,7 +403,9 @@ gulp.task('add-example-boilerplate', function() { // copies boilerplate files to locations // where an example app is found -gulp.task('_copy-example-boilerplate', copyExampleBoilerplate); +gulp.task('_copy-example-boilerplate', function () { + if (!argv.fast) copyExampleBoilerplate(); +}); // copies boilerplate files to locations @@ -451,6 +453,36 @@ gulp.task('remove-example-boilerplate', function() { deleteExampleBoilerPlate(); }); +// Npm install Angular libraries into examples/node_modules, +// either release or current build packages +// Examples: +// gulp install-example-angular --build // use current build packages +// gulp install-example-angular // restore release packages +gulp.task('install-example-angular', installExampleAngular); + +function installExampleAngular() { + var sources; + var template; + var libs = [ + 'core', 'common', 'compiler', + 'platform-browser', 'platform-browser-dynamic', + 'forms', 'http', 'router', 'upgrade']; + + // Like: "angular/core-builds" or "@angular/core" + sources = libs.map( lib => argv.build ? `angular/${lib}-builds` : `@angular/${lib}`); + + sources.push('@angular/router-deprecated'); + + gutil.log(`Installing Angular npm packages from ${argv.build ? 'BUILD' : 'RELEASE'}`); + + var spawnInfo = spawnExt('rm', ['-rf', 'node_modules/@angular'], { cwd: EXAMPLES_PATH}); + return spawnInfo.promise + .then(() => { + spawnInfo = spawnExt('npm', ['install', ...sources], {cwd: EXAMPLES_PATH}); + return spawnInfo.promise + }); +} + // deletes boilerplate files that were added by copyExampleBoilerplate // from locations where an example app is found gulp.task('_delete-example-boilerplate', deleteExampleBoilerPlate); @@ -564,16 +596,16 @@ gulp.task('git-changed-examples', ['_shred-devguide-examples'], function(){ }); }); +gulp.task('harp-compile', ['build-docs'], function() { + return harpCompile(); +}); + gulp.task('check-deploy', ['build-docs'], function() { return harpCompile().then(function() { gutil.log('compile ok'); - if(argv.dryRun) { - return false; - } else { - gutil.log('running live server ...'); - execPromise('npm run live-server ./www'); - return askDeploy(); - } + gutil.log('running live server ...'); + execPromise('npm run live-server ./www'); + return askDeploy(); }).then(function(shouldDeploy) { if (shouldDeploy) { gutil.log('deploying...'); @@ -596,7 +628,18 @@ gulp.task('test-api-builder', function (cb) { // angular.io: gulp link-checker // local site: gulp link-checker --url=http://localhost:3000 gulp.task('link-checker', function(done) { - return linkChecker(); + var method = 'get'; // the default 'head' fails for some sites + var exclude = [ + // Dart API docs aren't working yet; ignore them + '*/dart/latest/api/*', + // Somehow the link checker sees ng1 {{...}} in the resource page; ignore it + 'resources/%7B%7Bresource.url%7D%7D', + // API docs have links directly into GitHub repo sources; these can + // quickly become invalid, so ignore them for now: + '*/angular/tree/*' + ]; + var blcOptions = { requestMethod: method, excludedKeywords: exclude}; + return linkChecker({ blcOptions: blcOptions }); }); @@ -727,12 +770,8 @@ function linkChecker(options) { var blcOptions = options.blcOptions || {}; var customData = options.customData || {}; - var excludeBad; // don't bother reporting bad links matching this RegExp - if (argv.excludeBad) { - excludeBad = new RegExp(argv.excludeBad); - } else { - excludeBad = options.excludeBad === undefined ? /docs\/dart\/latest\/api/ : ''; - } + // don't bother reporting bad links matching this RegExp + var excludeBad = argv.excludeBad ? new RegExp(argv.excludeBad) : (options.excludeBad || ''); var previousPage; var siteUrl = argv.url || options.url || 'https://angular.io/'; @@ -779,7 +818,8 @@ function linkChecker(options) { var outputFile = path.join(process.cwd(), 'link-checker-results.txt'); var header = 'Link checker results for: ' + siteUrl + '\nStarted: ' + (new Date()).toLocaleString() + - '\nSkipping bad links matching regex: ' +excludeBad.toString() + '\n\n'; + '\nExcluded links (blc file globs): ' + blcOptions.excludedKeywords + + '\nExcluded links (custom --exclude-bad regex): ' + excludeBad.toString() + '\n\n'; gutil.log(header); fs.writeFileSync(outputFile, header); @@ -1030,7 +1070,7 @@ function buildApiDocs(targetLanguage) { try { // Build a specialized package to generate different versions of the API docs var package = new Package('apiDocs', [require(path.resolve(TOOLS_PATH, 'api-builder/angular.io-package'))]); - package.config(function(log, targetEnvironments, writeFilesProcessor, readTypeScriptModules) { + package.config(function(log, targetEnvironments, writeFilesProcessor, readTypeScriptModules, linkDocsInlineTagDef) { log.level = _dgeniLogLevel; ALLOWED_LANGUAGES.forEach(function(target) { targetEnvironments.addAllowed(target); }); if (targetLanguage) { @@ -1040,7 +1080,9 @@ function buildApiDocs(targetLanguage) { // Don't read TypeScript modules if we are not generating API docs - Dart I am looking at you! readTypeScriptModules.$enabled = false; } - writeFilesProcessor.outputFolder = targetLanguage + '/latest/api'; + linkDocsInlineTagDef.lang = targetLanguage; + linkDocsInlineTagDef.vers = 'latest'; + writeFilesProcessor.outputFolder = path.join(targetLanguage, linkDocsInlineTagDef.vers, 'api'); } }); diff --git a/package.json b/package.json index 2645f4a367..44d1d2da1a 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "codelyzer": "0.0.22", "del": "^2.2.0", "dgeni": "^0.4.0", - "dgeni-packages": "^0.13.0", + "dgeni-packages": "^0.13.1", "diff": "^2.1.3", "fs-extra": "^0.30.0", "globby": "^4.0.0", diff --git a/public/docs/_examples/animations/e2e-spec.ts b/public/docs/_examples/animations/e2e-spec.ts index 2450e7699b..e2e9b64b9b 100644 --- a/public/docs/_examples/animations/e2e-spec.ts +++ b/public/docs/_examples/animations/e2e-spec.ts @@ -255,7 +255,7 @@ describe('Animation Tests', () => { expect(li.getCssValue('transform')).toMatch(NO_TRANSFORM_MATRIX_REGEX); expect(li.getCssValue('opacity')).toMatch('1'); - removeHero(); + removeHero(700); expect(li.isPresent()).toBe(false); }); @@ -289,19 +289,22 @@ describe('Animation Tests', () => { }); }); - function addActiveHero() { + function addActiveHero(sleep?: number) { + sleep = sleep || 500; element(by.buttonText('Add active hero')).click(); - browser.driver.sleep(500); + browser.driver.sleep(sleep); } - function addInactiveHero() { + function addInactiveHero(sleep?: number) { + sleep = sleep || 500; element(by.buttonText('Add inactive hero')).click(); - browser.driver.sleep(500); + browser.driver.sleep(sleep); } - function removeHero() { + function removeHero(sleep?: number) { + sleep = sleep || 500; element(by.buttonText('Remove hero')).click(); - browser.driver.sleep(500); + browser.driver.sleep(sleep); } function getScaleX(el: protractor.ElementFinder) { diff --git a/public/docs/_examples/architecture/dart/lib/app_component.dart b/public/docs/_examples/architecture/dart/lib/app_component.dart new file mode 100644 index 0000000000..d0253504b4 --- /dev/null +++ b/public/docs/_examples/architecture/dart/lib/app_component.dart @@ -0,0 +1,15 @@ +// #docregion import +import 'package:angular2/core.dart'; +// #enddocregion import + +import 'hero_list_component.dart'; +import 'sales_tax_component.dart'; + +@Component( + selector: 'my-app', + template: ''' + + ''', + directives: const [HeroListComponent, SalesTaxComponent]) +// #docregion export +class AppComponent { } diff --git a/public/docs/_examples/architecture/dart/lib/backend_service.dart b/public/docs/_examples/architecture/dart/lib/backend_service.dart index d0f6886dbf..ef46001648 100644 --- a/public/docs/_examples/architecture/dart/lib/backend_service.dart +++ b/public/docs/_examples/architecture/dart/lib/backend_service.dart @@ -1,4 +1,5 @@ -// #docregion +import 'dart:async'; + import 'package:angular2/core.dart'; import 'hero.dart'; @@ -6,19 +7,22 @@ import 'logger_service.dart'; @Injectable() class BackendService { + static final _mockHeroes = [ + new Hero('Windstorm', 'Weather mastery'), + new Hero('Mr. Nice', 'Killing them with kindness'), + new Hero('Magneta', 'Manipulates metalic objects') + ]; + final Logger _logger; - List getAll(type) { - // TODO get from the database and return as a promise - if (type == Hero) { - return [ - new Hero('Windstorm', power: 'Weather mastery'), - new Hero('Mr. Nice', power: 'Killing them with kindness'), - new Hero('Magneta', power: 'Manipulates metalic objects') - ]; - } - _logger.error('Cannot get object of this type'); - throw new ArgumentError("TODO: put log content here"); - } BackendService(Logger this._logger); + + Future getAll(type) { + // TODO get from the database + if (type == Hero) return new Future.value(_mockHeroes); + + var msg = 'Cannot get object of this type'; + _logger.error(msg); + throw new Exception(msg); + } } diff --git a/public/docs/_examples/architecture/dart/lib/hero.dart b/public/docs/_examples/architecture/dart/lib/hero.dart index 1aa20e0ee8..8b9bf3dff3 100644 --- a/public/docs/_examples/architecture/dart/lib/hero.dart +++ b/public/docs/_examples/architecture/dart/lib/hero.dart @@ -1,11 +1,7 @@ -// #docregion class Hero { static int _nextId = 1; - int id; - String name; - String power; + final int id; + String name, power; - Hero(this.name, {this.power}) { - id = _nextId++; - } + Hero(this.name, [this.power = '']) : id = _nextId++; } diff --git a/public/docs/_examples/architecture/dart/lib/hero_detail_component.dart b/public/docs/_examples/architecture/dart/lib/hero_detail_component.dart index 94c26abbd0..16a22159d8 100644 --- a/public/docs/_examples/architecture/dart/lib/hero_detail_component.dart +++ b/public/docs/_examples/architecture/dart/lib/hero_detail_component.dart @@ -1,9 +1,11 @@ -// #docregion import 'package:angular2/core.dart'; import 'hero.dart'; -@Component(selector: 'hero-detail', templateUrl: 'hero_detail_component.html') +@Component( + selector: 'hero-detail', + templateUrl: 'hero_detail_component.html') class HeroDetailComponent { - @Input() Hero hero; + @Input() + Hero hero; } diff --git a/public/docs/_examples/architecture/dart/lib/hero_detail_component.html b/public/docs/_examples/architecture/dart/lib/hero_detail_component.html index 7dde3107e4..224de8bb86 100644 --- a/public/docs/_examples/architecture/dart/lib/hero_detail_component.html +++ b/public/docs/_examples/architecture/dart/lib/hero_detail_component.html @@ -2,8 +2,8 @@

{{hero.name}} Detail

Id: {{hero.id}}
Name: - -
- -
Power: + + +
+
Power:
diff --git a/public/docs/_examples/architecture/dart/lib/hero_list_component.dart b/public/docs/_examples/architecture/dart/lib/hero_list_component.dart index ba2b75de70..8d30a2344e 100644 --- a/public/docs/_examples/architecture/dart/lib/hero_list_component.dart +++ b/public/docs/_examples/architecture/dart/lib/hero_list_component.dart @@ -1,4 +1,3 @@ -// #docplaster import 'package:angular2/core.dart'; import 'hero.dart'; @@ -6,32 +5,31 @@ import 'hero_detail_component.dart'; import 'hero_service.dart'; // #docregion metadata -// #docregion providers @Component( -// #enddocregion providers selector: 'hero-list', templateUrl: 'hero_list_component.html', directives: const [HeroDetailComponent], -// #docregion providers - providers: const [HeroService]) -// #enddocregion providers -// #enddocregion metadata -/* -// #docregion metadata, providers -class HeroListComponent { ... } -// #enddocregion metadata, providers -*/ + // #docregion providers + providers: const [HeroService] + // #enddocregion providers + ) // #docregion class -class HeroListComponent { +class HeroListComponent implements OnInit { + // #enddocregion metadata List heroes; Hero selectedHero; -// #docregion ctor - HeroListComponent(HeroService heroService) { - heroes = heroService.getHeroes(); + // #docregion ctor + final HeroService _heroService; + + HeroListComponent(this._heroService); + // #enddocregion ctor + + void ngOnInit() { + heroes = _heroService.getHeroes(); } -// #enddocregion ctor - selectHero(Hero hero) { + + void selectHero(Hero hero) { selectedHero = hero; } + // #docregion metadata } -// #enddocregion class diff --git a/public/docs/_examples/architecture/dart/lib/hero_list_component.html b/public/docs/_examples/architecture/dart/lib/hero_list_component.html index e493e949fb..de60c4e376 100644 --- a/public/docs/_examples/architecture/dart/lib/hero_list_component.html +++ b/public/docs/_examples/architecture/dart/lib/hero_list_component.html @@ -1,8 +1,11 @@

Hero List

-
- {{hero.name}} -
+

Pick a hero from the list

+ diff --git a/public/docs/_examples/architecture/dart/lib/hero_list_component_1.html b/public/docs/_examples/architecture/dart/lib/hero_list_component_1.html index 7a08bd2f8b..eef5b9a3bf 100644 --- a/public/docs/_examples/architecture/dart/lib/hero_list_component_1.html +++ b/public/docs/_examples/architecture/dart/lib/hero_list_component_1.html @@ -1,10 +1,9 @@ -
{{hero.name}}
- -
...
+
  • {{hero.name}}
  • + +
  • -
    ...
    - - +
  • + diff --git a/public/docs/_examples/architecture/dart/lib/hero_service.dart b/public/docs/_examples/architecture/dart/lib/hero_service.dart index 794e79927e..a23734d327 100644 --- a/public/docs/_examples/architecture/dart/lib/hero_service.dart +++ b/public/docs/_examples/architecture/dart/lib/hero_service.dart @@ -4,16 +4,20 @@ import 'backend_service.dart'; import 'hero.dart'; import 'logger_service.dart'; -// #docregion class @Injectable() +// #docregion class class HeroService { final BackendService _backendService; final Logger _logger; - HeroService(Logger this._logger, BackendService this._backendService); + final List heroes = []; + + HeroService(this._logger, this._backendService); + List getHeroes() { - List heroes = _backendService.getAll(Hero); - _logger.log('Got ${heroes.length} heroes from the server.'); + _backendService.getAll(Hero).then((heroes) { + _logger.log('Fetched ${heroes.length} heroes.'); + this.heroes.addAll(heroes); // fill cache + }); return heroes; } } -// #enddocregion class diff --git a/public/docs/_examples/architecture/dart/lib/logger_service.dart b/public/docs/_examples/architecture/dart/lib/logger_service.dart index 2d6d1c4f75..2b8e19fde5 100644 --- a/public/docs/_examples/architecture/dart/lib/logger_service.dart +++ b/public/docs/_examples/architecture/dart/lib/logger_service.dart @@ -1,16 +1,11 @@ -// #docregion import 'dart:html'; import 'package:angular2/core.dart'; -/// A service for logging messages of various types. -/// -/// We could switch this implementation to use package:logging. @Injectable() +// #docregion class class Logger { void log(Object msg) => window.console.log(msg); - void error(Object msg) => window.console.error(msg); - void warn(Object msg) => window.console.warn(msg); } diff --git a/public/docs/_examples/architecture/dart/lib/sales_tax_component.dart b/public/docs/_examples/architecture/dart/lib/sales_tax_component.dart new file mode 100644 index 0000000000..7f5ed5036c --- /dev/null +++ b/public/docs/_examples/architecture/dart/lib/sales_tax_component.dart @@ -0,0 +1,28 @@ +import 'package:angular2/core.dart'; + +import 'sales_tax_service.dart'; +import 'tax_rate_service.dart'; + +@Component( + selector: 'sales-tax', + template: ''' +

    Sales Tax Calculator

    + Amount: + +
    + The sales tax is + {{ getTax(amountBox.value) | currency:'USD':false:'1.2-2' }} + +
    + ''', + providers: const [SalesTaxService, TaxRateService]) +class SalesTaxComponent { + SalesTaxService _salesTaxService; + + SalesTaxComponent(this._salesTaxService) {} + + num getTax(dynamic /* String | num */ value) => + this._salesTaxService.getVAT(value); +} diff --git a/public/docs/_examples/architecture/dart/lib/sales_tax_service.dart b/public/docs/_examples/architecture/dart/lib/sales_tax_service.dart new file mode 100644 index 0000000000..126f2c3033 --- /dev/null +++ b/public/docs/_examples/architecture/dart/lib/sales_tax_service.dart @@ -0,0 +1,14 @@ +import 'package:angular2/core.dart'; + +import 'tax_rate_service.dart'; + +@Injectable() +class SalesTaxService { + TaxRateService rateService; + + SalesTaxService(this.rateService); + + num getVAT(dynamic /* String | num */ value) => + rateService.getRate('VAT') * + (value is num ? value : num.parse(value, (_) => 0)); +} diff --git a/public/docs/_examples/architecture/dart/lib/tax_rate_service.dart b/public/docs/_examples/architecture/dart/lib/tax_rate_service.dart new file mode 100644 index 0000000000..87613acc4d --- /dev/null +++ b/public/docs/_examples/architecture/dart/lib/tax_rate_service.dart @@ -0,0 +1,6 @@ +import 'package:angular2/core.dart'; + +@Injectable() +class TaxRateService { + getRate(String rateName) => 0.10; +} diff --git a/public/docs/_examples/architecture/dart/web/index.html b/public/docs/_examples/architecture/dart/web/index.html index 17a00ffbed..a422e64542 100644 --- a/public/docs/_examples/architecture/dart/web/index.html +++ b/public/docs/_examples/architecture/dart/web/index.html @@ -1,12 +1,15 @@ - - Intro to Angular 2 - - - - + + Architecture of Angular 2 + + + + + + + - Loading... + Loading... diff --git a/public/docs/_examples/architecture/dart/web/main.dart b/public/docs/_examples/architecture/dart/web/main.dart index acb382ab8e..78397625f8 100644 --- a/public/docs/_examples/architecture/dart/web/main.dart +++ b/public/docs/_examples/architecture/dart/web/main.dart @@ -1,13 +1,14 @@ // #docregion import 'package:angular2/platform/browser.dart'; - +// #docregion import +import 'package:developer_guide_intro/app_component.dart'; +// #enddocregion import import 'package:developer_guide_intro/backend_service.dart'; -import 'package:developer_guide_intro/hero_list_component.dart'; import 'package:developer_guide_intro/hero_service.dart'; import 'package:developer_guide_intro/logger_service.dart'; -main() { +void main() { // #docregion bootstrap - bootstrap(HeroListComponent, [BackendService, HeroService, Logger]); + bootstrap(AppComponent, [BackendService, HeroService, Logger]); // #enddocregion bootstrap } diff --git a/public/docs/_examples/architecture/e2e-spec.ts b/public/docs/_examples/architecture/e2e-spec.ts index 152ffd322d..54bf9be8f2 100644 --- a/public/docs/_examples/architecture/e2e-spec.ts +++ b/public/docs/_examples/architecture/e2e-spec.ts @@ -1,63 +1,99 @@ /// 'use strict'; -describe('Architecture', function () { - let title = 'Hero List'; +const nameSuffix = 'X'; - beforeAll(function () { - browser.get(''); +class Hero { + id: number; + name: string; +} + +describe('Architecture', () => { + + const expectedTitle = 'Architecture of Angular 2'; + const expectedH2 = ['Hero List', 'Sales Tax Calculator']; + + beforeAll(() => browser.get('')); + + it(`has title '${expectedTitle}'`, () => { + expect(browser.getTitle()).toEqual(expectedTitle); }); - function itReset(name: string, func: () => any) { - it(name, function() { - browser.get('').then(func); - }); - } - - it(`should display correct title: ${title}`, function () { - expect(element(by.css('h2')).getText()).toEqual(title); - }); - - it('should display correct detail after selection', function() { - let detailView = element(by.css('hero-detail')); - expect(detailView.isPresent()).toBe(false); - // select the 2nd element - let selectEle = element.all(by.css('hero-list > div')).get(1); - selectEle.click().then(function() { - return selectEle.getText(); - }).then(function(selectedHeroName) { - // works but too specific if we change the app - // expect(selectedHeroName).toEqual('Mr. Nice'); - expect(detailView.isDisplayed()).toBe(true); - let detailTitleEle = element(by.css('hero-detail > h4')); - expect(detailTitleEle.getText()).toContain(selectedHeroName); - }); - }); - - itReset('should display correct detail after modification', function() { - let detailView = element(by.css('hero-detail')); - expect(detailView.isPresent()).toBe(false); - // select the 2nd element - let selectEle = element.all(by.css('hero-list > div')).get(1); - selectEle.click().then(function () { - return selectEle.getText(); - }).then(function (selectedHeroName) { - let detailTitleEle = element(by.css('hero-detail > h4')); - expect(detailTitleEle.getText()).toContain(selectedHeroName); - let heroNameEle = element.all(by.css('hero-detail input')).get(0); - - // check that both the initial selected item and the detail title reflect changes - // made to the input box. - // heroNameEle.sendKeys('foo'); - sendKeys(heroNameEle, 'foo'); - expect(detailTitleEle.getText()).toContain('foo'); - expect(selectEle.getText()).toContain('foo'); - - // getText on an input element always returns null - // http://stackoverflow.com/questions/20310442/how-to-gettext-on-an-input-in-protractor - // expect(heroNameEle.getText()).toEqual(selectedHeroName); - expect(heroNameEle.getAttribute('value')).toEqual(selectedHeroName + 'foo'); - }); + it(`has h2 '${expectedH2}'`, () => { + let h2 = element.all(by.css('h2')).map((elt) => elt.getText()); + expect(h2).toEqual(expectedH2); }); + describe('Hero', heroTests); + describe('Salex tax', salesTaxTests); }); + +function heroTests() { + + const targetHero: Hero = { id: 2, name: 'Mr. Nice' }; + + it('has the right number of heroes', () => { + let page = getPageElts(); + expect(page.heroes.count()).toEqual(3); + }); + + it('has no hero details initially', function () { + let page = getPageElts(); + expect(page.heroDetail.isPresent()).toBeFalsy('no hero detail'); + }); + + it('shows selected hero details', async () => { + await element(by.cssContainingText('li', targetHero.name)).click(); + let page = getPageElts(); + let hero = await heroFromDetail(page.heroDetail); + expect(hero.id).toEqual(targetHero.id); + expect(hero.name).toEqual(targetHero.name); + }); + + it(`shows updated hero name in details`, async () => { + let input = element.all(by.css('input')).first(); + await sendKeys(input, nameSuffix); + let page = getPageElts(); + let hero = await heroFromDetail(page.heroDetail); + let newName = targetHero.name + nameSuffix; + expect(hero.id).toEqual(targetHero.id); + expect(hero.name).toEqual(newName); + }); +} + +function salesTaxTests() { + it('has no sales tax initially', function () { + let page = getPageElts(); + expect(page.salesTaxDetail.isPresent()).toBeFalsy('no sales tax info'); + }); + + it('shows sales tax', async function () { + let page = getPageElts(); + await sendKeys(page.salesTaxAmountInput, '10'); + // Note: due to Dart bug USD is shown instead of $ + let re = /The sales tax is (\$|USD)1.00/; + expect(page.salesTaxDetail.getText()).toMatch(re); + }); +} + +// Helper functions + +function getPageElts() { + return { + heroes: element.all(by.css('my-app li')), + heroDetail: element(by.css('my-app hero-detail')), + salesTaxAmountInput: element(by.css('my-app sales-tax input')), + salesTaxDetail: element(by.css('my-app sales-tax div')) + }; +} + +async function heroFromDetail(detail: protractor.ElementFinder): Promise { + // Get hero id from the first
    + let _id = await detail.all(by.css('div')).first().getText(); + // Get name from the h2 + let _name = await detail.element(by.css('h4')).getText(); + return { + id: +_id.substr(_id.indexOf(' ') + 1), + name: _name.substr(0, _name.lastIndexOf(' ')) + }; +} diff --git a/public/docs/_examples/architecture/ts/app/hero-detail.component.html b/public/docs/_examples/architecture/ts/app/hero-detail.component.html index baa3e8d9f9..224de8bb86 100644 --- a/public/docs/_examples/architecture/ts/app/hero-detail.component.html +++ b/public/docs/_examples/architecture/ts/app/hero-detail.component.html @@ -2,8 +2,8 @@

    {{hero.name}} Detail

    Id: {{hero.id}}
    Name: - - - + + +
    -
    Power:
    \ No newline at end of file +
    Power:
    diff --git a/public/docs/_examples/architecture/ts/app/hero-list.component.1.html b/public/docs/_examples/architecture/ts/app/hero-list.component.1.html index d4d6b905fa..c6e6dd5133 100644 --- a/public/docs/_examples/architecture/ts/app/hero-list.component.1.html +++ b/public/docs/_examples/architecture/ts/app/hero-list.component.1.html @@ -1,12 +1,9 @@ -
    {{hero.name}}
    +
  • {{hero.name}}
  • -
    - +
  • -
    +
  • - - diff --git a/public/docs/_examples/architecture/ts/app/hero-list.component.html b/public/docs/_examples/architecture/ts/app/hero-list.component.html index f3c92b3141..b46a307bd3 100644 --- a/public/docs/_examples/architecture/ts/app/hero-list.component.html +++ b/public/docs/_examples/architecture/ts/app/hero-list.component.html @@ -2,8 +2,10 @@

    Hero List

    Pick a hero from the list

    -
    - {{hero.name}} -
    +
      +
    • + {{hero.name}} +
    • +
    diff --git a/public/docs/_examples/architecture/ts/app/hero-list.component.ts b/public/docs/_examples/architecture/ts/app/hero-list.component.ts index d7bc9e75d9..eaa21b45fe 100644 --- a/public/docs/_examples/architecture/ts/app/hero-list.component.ts +++ b/public/docs/_examples/architecture/ts/app/hero-list.component.ts @@ -1,4 +1,3 @@ -// #docplaster import { Component, OnInit } from '@angular/core'; import { Hero } from './hero'; @@ -6,35 +5,28 @@ import { HeroDetailComponent } from './hero-detail.component'; import { HeroService } from './hero.service'; // #docregion metadata -// #docregion providers @Component({ -// #enddocregion providers selector: 'hero-list', templateUrl: 'app/hero-list.component.html', directives: [HeroDetailComponent], -// #docregion providers + // #docregion providers providers: [HeroService] + // #enddocregion providers }) -// #enddocregion providers -// #enddocregion metadata -/* -// #docregion metadata, providers -export class HeroesComponent { ... } -// #enddocregion metadata, providers -*/ // #docregion class export class HeroListComponent implements OnInit { + // #enddocregion metadata heroes: Hero[]; selectedHero: Hero; -// #docregion ctor + // #docregion ctor constructor(private service: HeroService) { } -// #enddocregion ctor + // #enddocregion ctor ngOnInit() { this.heroes = this.service.getHeroes(); } selectHero(hero: Hero) { this.selectedHero = hero; } + // #docregion metadata } -// #enddocregion class diff --git a/public/docs/_examples/architecture/ts/app/hero.service.ts b/public/docs/_examples/architecture/ts/app/hero.service.ts index 027d4bfc6a..493f064e40 100644 --- a/public/docs/_examples/architecture/ts/app/hero.service.ts +++ b/public/docs/_examples/architecture/ts/app/hero.service.ts @@ -9,11 +9,9 @@ import { Logger } from './logger.service'; export class HeroService { private heroes: Hero[] = []; - // #docregion ctor constructor( private backend: BackendService, private logger: Logger) { } - // #enddocregion ctor getHeroes() { this.backend.getAll(Hero).then( (heroes: Hero[]) => { @@ -23,4 +21,3 @@ export class HeroService { return this.heroes; } } -// #enddocregion class diff --git a/public/docs/_examples/architecture/ts/app/logger.service.ts b/public/docs/_examples/architecture/ts/app/logger.service.ts index 2d66d202eb..9277ee8bc0 100644 --- a/public/docs/_examples/architecture/ts/app/logger.service.ts +++ b/public/docs/_examples/architecture/ts/app/logger.service.ts @@ -1,4 +1,3 @@ -// #docregion import { Injectable } from '@angular/core'; @Injectable() @@ -8,4 +7,3 @@ export class Logger { error(msg: any) { console.error(msg); } warn(msg: any) { console.warn(msg); } } -// #enddocregion class diff --git a/public/docs/_examples/architecture/ts/app/sales-tax.component.ts b/public/docs/_examples/architecture/ts/app/sales-tax.component.ts index 252f8e5b14..983e731dc7 100644 --- a/public/docs/_examples/architecture/ts/app/sales-tax.component.ts +++ b/public/docs/_examples/architecture/ts/app/sales-tax.component.ts @@ -1,14 +1,9 @@ -// #docplaster -// #docregion import { Component } from '@angular/core'; import { SalesTaxService } from './sales-tax.service'; import { TaxRateService } from './tax-rate.service'; -// #docregion metadata -// #docregion providers @Component({ -// #enddocregion providers selector: 'sales-tax', template: `

    Sales Tax Calculator

    @@ -19,24 +14,12 @@ import { TaxRateService } from './tax-rate.service'; {{ getTax(amountBox.value) | currency:'USD':true:'1.2-2' }}
    `, -// #docregion providers providers: [SalesTaxService, TaxRateService] }) -// #enddocregion providers -// #enddocregion metadata -/* -// #docregion metadata, providers -export class SalesTaxComponent { ... } -// #enddocregion metadata, providers -*/ -// #docregion class export class SalesTaxComponent { -// #docregion ctor constructor(private salesTaxService: SalesTaxService) { } -// #enddocregion ctor getTax(value: string | number) { return this.salesTaxService.getVAT(value); } } -// #enddocregion class diff --git a/public/docs/_examples/architecture/ts/app/sales-tax.service.ts b/public/docs/_examples/architecture/ts/app/sales-tax.service.ts index 5bf505548a..d859dc1595 100644 --- a/public/docs/_examples/architecture/ts/app/sales-tax.service.ts +++ b/public/docs/_examples/architecture/ts/app/sales-tax.service.ts @@ -1,20 +1,14 @@ -// #docregion import { Injectable } from '@angular/core'; import { TaxRateService } from './tax-rate.service'; -// #docregion class @Injectable() export class SalesTaxService { constructor(private rateService: TaxRateService) { } + getVAT(value: string | number) { - let amount: number; - if (typeof value === 'string') { - amount = parseFloat(value); - } else { - amount = value; - } + let amount = (typeof value === 'string') ? + parseFloat(value) : value; return (amount || 0) * this.rateService.getRate('VAT'); } } -// #enddocregion class diff --git a/public/docs/_examples/architecture/ts/app/tax-rate.service.ts b/public/docs/_examples/architecture/ts/app/tax-rate.service.ts index 334399f27a..fff2f4df8f 100644 --- a/public/docs/_examples/architecture/ts/app/tax-rate.service.ts +++ b/public/docs/_examples/architecture/ts/app/tax-rate.service.ts @@ -1,9 +1,6 @@ -// #docregion import { Injectable } from '@angular/core'; -// #docregion class @Injectable() export class TaxRateService { - getRate(rateName: string) {return 0.10; } // always 10% everywhere + getRate(rateName: string) { return 0.10; } // 10% everywhere } -// #enddocregion class diff --git a/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.component.html b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.component.html index 9626afd5cb..72fd3de86f 100644 --- a/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.component.html +++ b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.component.html @@ -4,7 +4,7 @@

    Routed Movies

    diff --git a/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.component.ts b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.component.ts index f2ca099297..cdb9d9fd81 100644 --- a/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.component.ts +++ b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.component.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core'; -import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from '@angular/router-deprecated'; +import { ROUTER_DIRECTIVES } from '@angular/router'; import { MovieListComponent } from './movie-list.component'; import { MovieService } from './movie.service'; @@ -12,11 +12,8 @@ import { StringSafeDatePipe } from './date.pipe'; styleUrls: ['app/app.component.css'], directives: [MovieListComponent, ROUTER_DIRECTIVES], pipes: [StringSafeDatePipe], - providers: [MovieService, ROUTER_PROVIDERS] + providers: [MovieService] }) -@RouteConfig([ - {path: '/movies', name: 'Movies', component: MovieListComponent, useAsDefault: true} -]) export class AppComponent { angularDocsUrl = 'https://angular.io/'; diff --git a/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.routes.ts b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.routes.ts new file mode 100644 index 0000000000..1fd2627996 --- /dev/null +++ b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.routes.ts @@ -0,0 +1,13 @@ +// #docregion +import { provideRouter, RouterConfig } from '@angular/router'; + +import { MovieListComponent } from './movie-list.component'; + +const routes: RouterConfig = [ + { path: '', redirectTo: '/movies', pathMatch: 'full' }, + { path: 'movies', component: MovieListComponent } +]; + +export const appRouterProviders = [ + provideRouter(routes) +]; diff --git a/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/main.1.ts b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/main.1.ts new file mode 100644 index 0000000000..67de525937 --- /dev/null +++ b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/main.1.ts @@ -0,0 +1,5 @@ +// #docregion +import { bootstrap } from '@angular/platform-browser-dynamic'; +import { AppComponent } from './app.component'; + +bootstrap(AppComponent); diff --git a/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/main.ts b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/main.ts index 52b47899ef..7a1d58fad8 100644 --- a/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/main.ts +++ b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/main.ts @@ -1,6 +1,8 @@ // #docregion import { bootstrap } from '@angular/platform-browser-dynamic'; - import { AppComponent } from './app.component'; +import { appRouterProviders } from './app.routes'; -bootstrap(AppComponent); +bootstrap(AppComponent, [ + appRouterProviders +]); diff --git a/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/movie-list.component.ts b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/movie-list.component.ts index 4157d8aa3a..67b0c2873d 100644 --- a/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/movie-list.component.ts +++ b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/movie-list.component.ts @@ -2,7 +2,6 @@ // #docplaster // #docregion import import { Component } from '@angular/core'; -import { ROUTER_DIRECTIVES } from '@angular/router-deprecated'; // #enddocregion import import { MovieService } from './movie.service'; import { IMovie } from './movie'; diff --git a/public/docs/_examples/cb-dependency-injection/ts/app/app.routes.ts b/public/docs/_examples/cb-dependency-injection/ts/app/app.routes.ts new file mode 100644 index 0000000000..c8eb6ee683 --- /dev/null +++ b/public/docs/_examples/cb-dependency-injection/ts/app/app.routes.ts @@ -0,0 +1,7 @@ +import { provideRouter, RouterConfig } from '@angular/router'; + +const routes: RouterConfig = []; + +export const appRouterProviders = [ + provideRouter(routes) +]; diff --git a/public/docs/_examples/cb-dependency-injection/ts/app/main.ts b/public/docs/_examples/cb-dependency-injection/ts/app/main.ts index fd4646a055..c83f329e91 100644 --- a/public/docs/_examples/cb-dependency-injection/ts/app/main.ts +++ b/public/docs/_examples/cb-dependency-injection/ts/app/main.ts @@ -1,7 +1,7 @@ // #docregion -import { bootstrap } from '@angular/platform-browser-dynamic'; -import { XHRBackend } from '@angular/http'; -import { ROUTER_PROVIDERS } from '@angular/router-deprecated'; +import { bootstrap } from '@angular/platform-browser-dynamic'; +import { XHRBackend } from '@angular/http'; +import { appRouterProviders } from './app.routes'; import { LocationStrategy, HashLocationStrategy } from '@angular/common'; @@ -13,7 +13,7 @@ import { AppComponent } from './app.component'; // #docregion bootstrap bootstrap(AppComponent, [ - ROUTER_PROVIDERS, + appRouterProviders, { provide: LocationStrategy, useClass: HashLocationStrategy }, { provide: XHRBackend, useClass: InMemoryBackendService }, // in-mem server diff --git a/public/docs/_examples/router/ts/app/app.component.5.ts b/public/docs/_examples/router/ts/app/app.component.5.ts deleted file mode 100644 index 7ffb615027..0000000000 --- a/public/docs/_examples/router/ts/app/app.component.5.ts +++ /dev/null @@ -1,42 +0,0 @@ -// #docplaster -// #docregion -import { Component } from '@angular/core'; - -import { provideRouter, ROUTER_DIRECTIVES } from '@angular/router'; -import { routes } from './app.routes'; -// #docregion can-deactivate-guard -import { CanDeactivateGuard } from './interfaces'; -// #enddocregion can-deactivate-guard - -import { DialogService } from './dialog.service'; -import { HeroService } from './heroes/hero.service'; - -// Add these symbols to override the `LocationStrategy` -import { LocationStrategy, - HashLocationStrategy } from '@angular/common'; - - -@Component({ - selector: 'my-app', -// #docregion template - template: ` -

    Component Router

    - - - `, -// #enddocregion template - providers: [ - HeroService, - DialogService, - provideRouter(routes), - CanDeactivateGuard, - { provide: LocationStrategy, - useClass: HashLocationStrategy } // .../#/crisis-center/ - ], - directives: [ROUTER_DIRECTIVES] -}) -export class AppComponent { -} diff --git a/public/docs/_examples/router/ts/app/app.routes.1.ts b/public/docs/_examples/router/ts/app/app.routes.1.ts index f22d480744..14a0ebe946 100644 --- a/public/docs/_examples/router/ts/app/app.routes.1.ts +++ b/public/docs/_examples/router/ts/app/app.routes.1.ts @@ -14,7 +14,7 @@ import { HeroDetailComponent } from './heroes/hero-detail.component'; // #docregion // #docregion route-config -export const routes: RouterConfig = [ +const routes: RouterConfig = [ // #docregion route-defs { path: 'crisis-center', component: CrisisCenterComponent }, { path: 'heroes', component: HeroListComponent }, diff --git a/public/docs/_examples/router/ts/app/app.routes.2.ts b/public/docs/_examples/router/ts/app/app.routes.2.ts index c79e8ad17c..e2e3d1f9fc 100644 --- a/public/docs/_examples/router/ts/app/app.routes.2.ts +++ b/public/docs/_examples/router/ts/app/app.routes.2.ts @@ -8,7 +8,7 @@ import { CrisisListComponent } from './crisis-list.component'; import { HeroListComponent } from './hero-list.component'; // #docregion route-config -export const routes: RouterConfig = [ +const routes: RouterConfig = [ { path: 'crisis-center', component: CrisisListComponent }, { path: 'heroes', component: HeroListComponent } ]; diff --git a/public/docs/_examples/style-guide/ts/app/app.routes.ts b/public/docs/_examples/style-guide/ts/app/app.routes.ts index 9254169331..644e86bffd 100644 --- a/public/docs/_examples/style-guide/ts/app/app.routes.ts +++ b/public/docs/_examples/style-guide/ts/app/app.routes.ts @@ -28,6 +28,7 @@ import { AppComponent as S0704 } from '../07-04/app'; import { AppComponent as S0901 } from '../09-01/app'; const routes: RouterConfig = [ + { path: '', redirectTo: '/01-01', pathMatch: 'full' }, { path: '01-01', component: S0101 }, { path: '02-07', component: S0207 }, { path: '02-08', component: S0208 }, @@ -56,6 +57,6 @@ const routes: RouterConfig = [ { path: '09-01', component: S0901 }, ]; -export const APP_ROUTER_PROVIDERS = [ +export const appRouterProviders = [ provideRouter(routes) ]; diff --git a/public/docs/_examples/style-guide/ts/app/main.ts b/public/docs/_examples/style-guide/ts/app/main.ts index b58fbb68dd..759f9e261b 100644 --- a/public/docs/_examples/style-guide/ts/app/main.ts +++ b/public/docs/_examples/style-guide/ts/app/main.ts @@ -4,12 +4,12 @@ import { HashLocationStrategy, LocationStrategy } from '@angular/common'; import { InMemoryBackendService, SEED_DATA } from 'angular2-in-memory-web-api'; import 'rxjs/add/operator/map'; -import { APP_ROUTER_PROVIDERS } from './app.routes'; +import { appRouterProviders } from './app.routes'; import { HeroData } from './hero-data'; import { AppComponent } from './app.component'; bootstrap(AppComponent, [ - APP_ROUTER_PROVIDERS, + appRouterProviders, HTTP_PROVIDERS, { provide: LocationStrategy, useClass: HashLocationStrategy }, { provide: XHRBackend, useClass: InMemoryBackendService }, diff --git a/public/docs/_examples/testing/ts/app/app.component.spec.ts b/public/docs/_examples/testing/ts/app/app.component.spec.ts index 9cf6d82e1a..f6c30b0e76 100644 --- a/public/docs/_examples/testing/ts/app/app.component.spec.ts +++ b/public/docs/_examples/testing/ts/app/app.component.spec.ts @@ -5,13 +5,10 @@ import { By } from '@angular/platform-browser'; import { DebugElement } from '@angular/core'; import { - beforeEach, beforeEachProviders, - describe, ddescribe, xdescribe, - expect, it, iit, xit, async, inject } from '@angular/core/testing'; -import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing'; +import { ComponentFixture, TestComponentBuilder } from '@angular/core/testing'; import { Hero, HeroService, MockHeroService } from './mock-hero.service'; @@ -45,7 +42,7 @@ describe('AppComponent', () => { it('can get title from template', () => { fixture.detectChanges(); let titleEl = fixture.debugElement.query(By.css('h1')).nativeElement; - expect(titleEl).toHaveText(comp.title); + expect(titleEl.textContent).toContain(comp.title); }); it('can get RouterLinks from template', () => { diff --git a/public/docs/_examples/testing/ts/app/bad-tests.spec.ts b/public/docs/_examples/testing/ts/app/bad-tests.spec.ts index 1251185160..d73882372c 100644 --- a/public/docs/_examples/testing/ts/app/bad-tests.spec.ts +++ b/public/docs/_examples/testing/ts/app/bad-tests.spec.ts @@ -19,13 +19,11 @@ import { DebugElement } from '@angular/core'; import { By } from '@angular/platform-browser'; import { - beforeEach, beforeEachProviders, - describe, ddescribe, xdescribe, - expect, it, iit, xit, + addProviders, async, inject } from '@angular/core/testing'; -import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing'; +import { ComponentFixture, TestComponentBuilder } from '@angular/core/testing'; import { ViewMetadata } from '@angular/core'; import { Observable } from 'rxjs/Rx'; @@ -143,20 +141,20 @@ xdescribe('async & inject testing errors', () => { restoreJasmineIt(); }, 10000); - describe('using beforeEachProviders', () => { - beforeEachProviders(() => [{ provide: FancyService, useValue: new FancyService() }]); + describe('using addProviders', () => { + addProviders([{ provide: FancyService, useValue: new FancyService() }]); beforeEach( inject([FancyService], (service: FancyService) => { expect(service.value).toEqual('real value'); })); - describe('nested beforeEachProviders', () => { + describe('nested addProviders', () => { it('should fail when the injector has already been used', () => { patchJasmineBeforeEach(); expect(() => { - beforeEachProviders(() => [{ provide: FancyService, useValue: new FancyService() }]); + addProviders([{ provide: FancyService, useValue: new FancyService() }]); }) - .toThrowError('beforeEachProviders was called after the injector had been used ' + + .toThrowError('addProviders was called after the injector had been used ' + 'in a beforeEach or it block. This invalidates the test injector'); restoreJasmineBeforeEach(); }); diff --git a/public/docs/_examples/testing/ts/app/bag.spec.ts b/public/docs/_examples/testing/ts/app/bag.spec.ts index 80790fca67..7218e91f1b 100644 --- a/public/docs/_examples/testing/ts/app/bag.spec.ts +++ b/public/docs/_examples/testing/ts/app/bag.spec.ts @@ -1,7 +1,7 @@ // Based on https://github.com/angular/angular/blob/master/modules/angular2/test/testing/testing_public_spec.ts /* tslint:disable */ import { - BadTemplateUrl, ButtonComp, + ButtonComp, ChildChildComp, ChildComp, ChildWithChildComp, ExternalTemplateComp, FancyService, MockFancyService, @@ -16,14 +16,12 @@ import { DebugElement } from '@angular/core'; import { By } from '@angular/platform-browser'; import { - beforeEach, beforeEachProviders, - describe, ddescribe, xdescribe, - expect, it, iit, xit, - async, inject, + addProviders, + inject, async, fakeAsync, tick, withProviders } from '@angular/core/testing'; -import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing'; +import { ComponentFixture, TestComponentBuilder } from '@angular/core/testing'; import { ViewMetadata } from '@angular/core'; @@ -31,47 +29,6 @@ import { Observable } from 'rxjs/Rx'; //////// SPECS ///////////// -/// Verify can use Angular testing's DOM abstraction to access DOM - -describe('angular2 jasmine matchers', () => { - describe('toHaveCssClass', () => { - it('should assert that the CSS class is present', () => { - let el = document.createElement('div'); - el.classList.add('bombasto'); - expect(el).toHaveCssClass('bombasto'); - }); - - it('should assert that the CSS class is not present', () => { - let el = document.createElement('div'); - el.classList.add('bombasto'); - expect(el).not.toHaveCssClass('fatias'); - }); - }); - - describe('toHaveCssStyle', () => { - it('should assert that the CSS style is present', () => { - let el = document.createElement('div'); - expect(el).not.toHaveCssStyle('width'); - - el.style.setProperty('width', '100px'); - expect(el).toHaveCssStyle('width'); - }); - - it('should assert that the styles are matched against the element', () => { - let el = document.createElement('div'); - expect(el).not.toHaveCssStyle({width: '100px', height: '555px'}); - - el.style.setProperty('width', '100px'); - expect(el).toHaveCssStyle({width: '100px'}); - expect(el).not.toHaveCssStyle({width: '100px', height: '555px'}); - - el.style.setProperty('height', '555px'); - expect(el).toHaveCssStyle({height: '555px'}); - expect(el).toHaveCssStyle({width: '100px', height: '555px'}); - }); - }); -}); - describe('using the async helper', () => { let actuallyDone = false; @@ -101,7 +58,7 @@ describe('using the async helper', () => { p.catch(() => { actuallyDone = true; }); })); - it('should run async test with successful Observable', async(() => { + xit('should run async test with successful Observable', async(() => { let source = Observable.of(true).delay(10); source.subscribe( val => {}, @@ -114,9 +71,11 @@ describe('using the async helper', () => { describe('using the test injector with the inject helper', () => { describe('setting up Providers with FancyService', () => { - beforeEachProviders(() => [ - { provide: FancyService, useValue: new FancyService() } - ]); + beforeEach(() => { + addProviders([ + { provide: FancyService, useValue: new FancyService() } + ]); + }); it('should use FancyService', inject([FancyService], (service: FancyService) => { @@ -142,7 +101,7 @@ describe('using the test injector with the inject helper', () => { ); }))); - it('test should wait for FancyService.getObservableDelayValue', + xit('test should wait for FancyService.getObservableDelayValue', async(inject([FancyService], (service: FancyService) => { service.getObservableDelayValue().subscribe( value => { expect(value).toEqual('observable delay value'); } @@ -197,7 +156,7 @@ describe('test component builder', function() { tcb.createAsync(ChildComp).then(fixture => { fixture.detectChanges(); - expect(fixture.nativeElement).toHaveText('Original Child'); + expect(fixture.nativeElement.textContent).toContain('Original Child'); }); }))); @@ -206,11 +165,11 @@ describe('test component builder', function() { tcb.createAsync(MyIfComp).then(fixture => { fixture.detectChanges(); - expect(fixture.nativeElement).toHaveText('MyIf()'); + expect(fixture.nativeElement.textContent).toContain('MyIf()'); fixture.debugElement.componentInstance.showMore = true; fixture.detectChanges(); - expect(fixture.nativeElement).toHaveText('MyIf(More)'); + expect(fixture.nativeElement.textContent).toContain('MyIf(More)'); }); }))); @@ -262,7 +221,7 @@ describe('test component builder', function() { .createAsync(MockChildComp) .then(fixture => { fixture.detectChanges(); - expect(fixture.nativeElement).toHaveText('Mock'); + expect(fixture.nativeElement.textContent).toContain('Mock'); }); }))); @@ -276,7 +235,7 @@ describe('test component builder', function() { .createAsync(ChildComp) .then(fixture => { fixture.detectChanges(); - expect(fixture.nativeElement).toHaveText('Modified Child'); + expect(fixture.nativeElement.textContent).toContain('Modified Child'); }); }))); @@ -288,7 +247,7 @@ describe('test component builder', function() { .createAsync(ParentComp) .then(fixture => { fixture.detectChanges(); - expect(fixture.nativeElement).toHaveText('Parent(Mock)'); + expect(fixture.nativeElement.textContent).toContain('Parent(Mock)'); }); }))); @@ -302,8 +261,8 @@ describe('test component builder', function() { .createAsync(ParentComp) .then(fixture => { fixture.detectChanges(); - expect(fixture.nativeElement) - .toHaveText('Parent(Original Child(ChildChild Mock))'); + expect(fixture.nativeElement.textContent) + .toContain('Parent(Original Child(ChildChild Mock))'); }); }))); @@ -318,8 +277,8 @@ describe('test component builder', function() { .createAsync(TestProvidersComp) .then(fixture => { fixture.detectChanges(); - expect(fixture.nativeElement) - .toHaveText('injected value: mocked out value'); + expect(fixture.nativeElement.textContent) + .toContain('injected value: mocked out value'); }); }))); @@ -333,8 +292,8 @@ describe('test component builder', function() { .createAsync(TestViewProvidersComp) .then(fixture => { fixture.detectChanges(); - expect(fixture.nativeElement) - .toHaveText('injected value: mocked out value'); + expect(fixture.nativeElement.textContent) + .toContain('injected value: mocked out value'); }); }))); @@ -344,8 +303,8 @@ describe('test component builder', function() { tcb.createAsync(ExternalTemplateComp) .then(fixture => { fixture.detectChanges(); - expect(fixture.nativeElement) - .toHaveText('from external template\n'); + expect(fixture.nativeElement.textContent) + .toContain('from external template\n'); }); })), 10000); // Long timeout because this test makes an actual XHR. diff --git a/public/docs/_examples/testing/ts/app/dashboard.component.spec.ts b/public/docs/_examples/testing/ts/app/dashboard.component.spec.ts index 803ea00535..1b573c32f3 100644 --- a/public/docs/_examples/testing/ts/app/dashboard.component.spec.ts +++ b/public/docs/_examples/testing/ts/app/dashboard.component.spec.ts @@ -4,13 +4,11 @@ import { DashboardComponent } from './dashboard.component'; import { By } from '@angular/platform-browser'; import { - beforeEach, beforeEachProviders, - describe, ddescribe, xdescribe, - expect, it, iit, xit, + addProviders, async, inject } from '@angular/core/testing'; -import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing'; +import { ComponentFixture, TestComponentBuilder } from '@angular/core/testing'; import { Hero, HeroService, MockHeroService } from './mock-hero.service'; import { Router, MockRouter } from './mock-router'; @@ -70,13 +68,13 @@ describe('DashboardComponent', () => { let comp: DashboardComponent; let mockHeroService: MockHeroService; - beforeEachProviders(() => { + beforeEach(() => { mockHeroService = new MockHeroService(); - return [ + addProviders([ { provide: Router, useClass: MockRouter}, { provide: MockRouter, useExisting: Router}, { provide: HeroService, useValue: mockHeroService } - ]; + ]); }); it('can instantiate it', @@ -138,8 +136,8 @@ describe('DashboardComponent', () => { expect(heroNames.length).toEqual(4, 'should display 4 heroes'); // the 4th displayed hero should be the 5th mock hero - expect(heroNames[3].nativeElement) - .toHaveText(mockHeroService.mockHeroes[4].name); + expect(heroNames[3].nativeElement.textContent) + .toContain(mockHeroService.mockHeroes[4].name); }); }); diff --git a/public/docs/_examples/testing/ts/app/expect-proper.ts b/public/docs/_examples/testing/ts/app/expect-proper.ts deleted file mode 100644 index 77f3deb928..0000000000 --- a/public/docs/_examples/testing/ts/app/expect-proper.ts +++ /dev/null @@ -1,9 +0,0 @@ -// See https://github.com/angular/angular/issues/9017 -import { expect as expectCore } from '@angular/core/testing'; -import { NgMatchers } from '@angular/platform-browser/testing'; - -export function expect(spy: Function): NgMatchers; -export function expect(actual: any): NgMatchers; -export function expect(actual: any): NgMatchers { - return expectCore(actual) as NgMatchers; -} diff --git a/public/docs/_examples/testing/ts/app/http-hero.service.spec.ts b/public/docs/_examples/testing/ts/app/http-hero.service.spec.ts index 0c79f7f3aa..375efde560 100644 --- a/public/docs/_examples/testing/ts/app/http-hero.service.spec.ts +++ b/public/docs/_examples/testing/ts/app/http-hero.service.spec.ts @@ -1,12 +1,10 @@ /* tslint:disable:no-unused-variable */ import { - beforeEach, beforeEachProviders, - describe, ddescribe, xdescribe, - expect, it, iit, xit, + addProviders, async, inject, withProviders } from '@angular/core/testing'; -import { TestComponentBuilder } from '@angular/compiler/testing'; +import { TestComponentBuilder } from '@angular/core/testing'; import { MockBackend, @@ -42,10 +40,12 @@ const makeResponseData = (data: {}) => {return { data }; }; //////// SPECS ///////////// describe('Http-HeroService (mockBackend)', () => { - beforeEachProviders(() => [ - HTTP_PROVIDERS, - { provide: XHRBackend, useClass: MockBackend } - ]); + beforeEach(() => { + addProviders([ + HTTP_PROVIDERS, + { provide: XHRBackend, useClass: MockBackend } + ]); + }); it('can instantiate service when inject service', withProviders(() => [HeroService]) diff --git a/public/docs/_examples/testing/ts/liteserver-test-config.json b/public/docs/_examples/testing/ts/liteserver-test-config.json new file mode 100644 index 0000000000..6b1a2b5466 --- /dev/null +++ b/public/docs/_examples/testing/ts/liteserver-test-config.json @@ -0,0 +1,3 @@ +{ + "startPath": "unit-tests.html" +} \ No newline at end of file diff --git a/public/docs/_examples/toh-4/ts/app/mock-heroes.ts b/public/docs/_examples/toh-4/ts/app/mock-heroes.ts index 69afde3d34..6f7c5d83a0 100644 --- a/public/docs/_examples/toh-4/ts/app/mock-heroes.ts +++ b/public/docs/_examples/toh-4/ts/app/mock-heroes.ts @@ -1,7 +1,7 @@ // #docregion import { Hero } from './hero'; -export var HEROES: Hero[] = [ +export const HEROES: Hero[] = [ {id: 11, name: 'Mr. Nice'}, {id: 12, name: 'Narco'}, {id: 13, name: 'Bombasto'}, diff --git a/public/docs/_examples/toh-5/ts/app/app.routes.1.ts b/public/docs/_examples/toh-5/ts/app/app.routes.1.ts index 8b4d582680..0c34add29c 100644 --- a/public/docs/_examples/toh-5/ts/app/app.routes.1.ts +++ b/public/docs/_examples/toh-5/ts/app/app.routes.1.ts @@ -6,7 +6,7 @@ import { HeroesComponent } from './heroes.component'; import { HeroDetailComponent } from './hero-detail.component'; // #enddocregion hero-detail-import -export const routes: RouterConfig = [ +const routes: RouterConfig = [ // #docregion redirect-route { path: '', @@ -32,6 +32,6 @@ export const routes: RouterConfig = [ } ]; -export const APP_ROUTER_PROVIDERS = [ +export const appRouterProviders = [ provideRouter(routes) ]; diff --git a/public/docs/_examples/toh-5/ts/app/app.routes.2.ts b/public/docs/_examples/toh-5/ts/app/app.routes.2.ts index 47311fcd8d..075cc37496 100644 --- a/public/docs/_examples/toh-5/ts/app/app.routes.2.ts +++ b/public/docs/_examples/toh-5/ts/app/app.routes.2.ts @@ -9,6 +9,6 @@ const routes: RouterConfig = [ } ]; -export const APP_ROUTER_PROVIDERS = [ +export const appRouterProviders = [ provideRouter(routes) ]; diff --git a/public/docs/_examples/toh-5/ts/app/app.routes.ts b/public/docs/_examples/toh-5/ts/app/app.routes.ts index 33097804eb..c6074c3607 100644 --- a/public/docs/_examples/toh-5/ts/app/app.routes.ts +++ b/public/docs/_examples/toh-5/ts/app/app.routes.ts @@ -7,7 +7,7 @@ import { HeroesComponent } from './heroes.component'; import { HeroDetailComponent } from './hero-detail.component'; // #enddocregion hero-detail-import -export const routes: RouterConfig = [ +const routes: RouterConfig = [ { path: '', redirectTo: '/dashboard', @@ -27,6 +27,6 @@ export const routes: RouterConfig = [ } ]; -export const APP_ROUTER_PROVIDERS = [ +export const appRouterProviders = [ provideRouter(routes) ]; diff --git a/public/docs/_examples/toh-5/ts/app/main.ts b/public/docs/_examples/toh-5/ts/app/main.ts index da35003df1..f04d528627 100644 --- a/public/docs/_examples/toh-5/ts/app/main.ts +++ b/public/docs/_examples/toh-5/ts/app/main.ts @@ -2,8 +2,8 @@ import { bootstrap } from '@angular/platform-browser-dynamic'; import { AppComponent } from './app.component'; -import { APP_ROUTER_PROVIDERS } from './app.routes'; +import { appRouterProviders } from './app.routes'; bootstrap(AppComponent, [ - APP_ROUTER_PROVIDERS + appRouterProviders ]); diff --git a/public/docs/_examples/toh-6/ts/app/app.routes.ts b/public/docs/_examples/toh-6/ts/app/app.routes.ts index 1d9adf33fd..e143b2a3bf 100644 --- a/public/docs/_examples/toh-6/ts/app/app.routes.ts +++ b/public/docs/_examples/toh-6/ts/app/app.routes.ts @@ -5,7 +5,7 @@ import { DashboardComponent } from './dashboard.component'; import { HeroesComponent } from './heroes.component'; import { HeroDetailComponent } from './hero-detail.component'; -export const routes: RouterConfig = [ +const routes: RouterConfig = [ { path: '', redirectTo: '/dashboard', @@ -25,6 +25,6 @@ export const routes: RouterConfig = [ } ]; -export const APP_ROUTER_PROVIDERS = [ +export const appRouterProviders = [ provideRouter(routes) ]; diff --git a/public/docs/_examples/toh-6/ts/app/main.ts b/public/docs/_examples/toh-6/ts/app/main.ts index 948e2ca5ba..bda66d0377 100644 --- a/public/docs/_examples/toh-6/ts/app/main.ts +++ b/public/docs/_examples/toh-6/ts/app/main.ts @@ -12,20 +12,20 @@ import { bootstrap } from '@angular/platform-browser-dynamic'; import { HTTP_PROVIDERS } from '@angular/http'; import { AppComponent } from './app.component'; -import { APP_ROUTER_PROVIDERS } from './app.routes'; +import { appRouterProviders } from './app.routes'; // #enddocregion v1, final /* // #docregion v1 bootstrap(AppComponent, [ - APP_ROUTER_PROVIDERS, + appRouterProviders, HTTP_PROVIDERS ]); // #enddocregion v1 */ // #docregion final bootstrap(AppComponent, [ - APP_ROUTER_PROVIDERS, + appRouterProviders, HTTP_PROVIDERS, { provide: XHRBackend, useClass: InMemoryBackendService }, // in-mem server { provide: SEED_DATA, useClass: InMemoryDataService } // in-mem server data diff --git a/public/docs/_examples/webpack/ts/package.webpack.json b/public/docs/_examples/webpack/ts/package.webpack.json index 8d7fa67496..e383ba3355 100644 --- a/public/docs/_examples/webpack/ts/package.webpack.json +++ b/public/docs/_examples/webpack/ts/package.webpack.json @@ -24,6 +24,7 @@ "zone.js": "0.6.12" }, "devDependencies": { + "angular2-template-loader": "^0.4.0", "css-loader": "^0.23.1", "extract-text-webpack-plugin": "^1.0.1", "file-loader": "^0.8.5", diff --git a/public/docs/dart/latest/cookbook/dynamic-form-deprecated.html b/public/docs/dart/latest/cookbook/dynamic-form-deprecated.html new file mode 100644 index 0000000000..f8df2a84a6 --- /dev/null +++ b/public/docs/dart/latest/cookbook/dynamic-form-deprecated.html @@ -0,0 +1 @@ +!= partial("../../../_includes/_ts-temp") \ No newline at end of file diff --git a/public/docs/dart/latest/guide/animations.jade b/public/docs/dart/latest/guide/animations.jade new file mode 100644 index 0000000000..6778b6af28 --- /dev/null +++ b/public/docs/dart/latest/guide/animations.jade @@ -0,0 +1 @@ +!= partial("../../../_includes/_ts-temp") diff --git a/public/docs/dart/latest/guide/architecture.jade b/public/docs/dart/latest/guide/architecture.jade index 2c6f1b04bc..f5f63c07a8 100644 --- a/public/docs/dart/latest/guide/architecture.jade +++ b/public/docs/dart/latest/guide/architecture.jade @@ -1,451 +1,72 @@ -include ../_util-fns +extends ../../../ts/latest/guide/architecture.jade + +block includes + include ../_util-fns + - var _library_module = 'library' + - var _at_angular = 'angular2' :marked Angular 2 is a framework to help us build client applications in HTML and either JavaScript or a language (like Dart or TypeScript) that compiles to JavaScript. - Angular 2 for Dart is published as the `angular2` package, which - (like many other Dart packages) is available via the Pub tool. - With Angular, we write applications by composing HTML *templates* with Angularized markup, - writing *component* classes to manage those templates, adding application logic in *services*, - and handing the top root component to Angular's *bootstrapper*. - - Angular takes over, presenting our application content in a browser and responding to user interactions - according to the instructions we provided. - - -:marked - Of course there is more to it than this. - We're cruising at high altitude in this overview. - We're looking for landmarks. We should expect the object below to be fuzzy and obscured by occasional clouds. - Details become more clear and precise when we land in the chapters themselves. -
    - -:marked - An Angular 2 for Dart application rests on seven main building blocks: - 1. [Components](#component) - 1. [Templates](#template) - 1. [Metadata](#metadata) - 1. [Data binding](#data-binding) - 1. [Directives](#directive) - 1. [Services](#service) - 1. [Dependency injection](#dependency-injection) - -figure - img(src="/resources/images/devguide/architecture/overview.png" alt="overview" style="margin-left:-40px;" width="700") -:marked - Learn these seven and we're on our way. - -.l-main-section - -:marked - ## Components -figure - img(src="/resources/images/devguide/architecture/hero-component.png" alt="Component" align="left" style="width:200px; margin-left:-40px;margin-right:10px" ) -:marked - A **component** controls a patch of screen real estate that we could call a *view*. - A set of navigation links, a list of heroes, a hero editor ... - they're all views controlled by components. - - We define a component's application logic — what it does to support the view — inside a class. - The class interacts with the view through an API of properties and methods. - - - A `HeroListComponent`, for example, might have a `heroes` property that returns an array of heroes - that it acquired from a service. - It might have a `selectHero()` method that sets a `selectedHero` property when the user clicks to choose a hero from that list. - The component might be a class like this: - -+makeExample('architecture/dart/lib/hero_list_component.dart', 'class', 'lib/hero_list_component.dart') -:marked - Angular creates, updates, and destroys components as the user moves through the application. - The developer can take action at each moment in this lifecycle through optional lifecycle hooks. - - -.l-sub-section +block angular-parts :marked - We may wonder who is calling the component's constructor? Who provides the service parameter? - For the moment, have faith that Angular will call the constructor and deliver an - appropriate `HeroService` when we need it. + Angular 2 for Dart is published as the `angular2` package, which + (like many other Dart packages) is available via the Pub tool. -.l-main-section - -:marked - ## Templates -figure - img(src="/resources/images/devguide/architecture/template.png" alt="Template" align="left" style="width:200px; margin-left:-40px;margin-right:10px" ) -:marked - We define a component's view with its companion **template**. A template is a form of HTML - that tells Angular how to render the component. +block modules-in-dart + .callout.is-helpful + header Dart difference: Modules are compilation units or packages + :marked + In this guide, the term _module_ refers to a Dart compilation unit, such + as a library, or a package. (If a Dart file has no `library` or `part` + directive, then that file itself is a library and thus a compilation + unit.) For more information about compilation units, see + the chapter on "Libraries and Scripts" in the + [Dart Language Specification](https://www.dartlang.org/docs/spec/). - A template looks like regular HTML much of the time ... and then it gets a bit strange. Here is a - template for our `HeroListComponent`: -+makeExample('architecture/dart/lib/hero_list_component.html', null, 'lib/hero_list_component.html') -:marked - This template features typical HTML elements like `

    ` and `
    `. - But what are `*ngFor`, {‌{hero.name}}, `(click)`, `[hero]`, and ``? - They're examples of Angular's template syntax. - We'll grow accustomed to that syntax and may even learn to love it. +block modules-are-optional + //- N/A - Take a look at the last line, - which has the `` tag. - That tag adds a custom element representing a component we haven't seen yet, - a `HeroDetailComponent`. +block export-qualifier + .callout.is-helpful + header Dart difference: Public names are exported by default + :marked + Contrary to TypeScript, a Dart library always exports all names and + declarations in its **public** namespace, making explicit `export` + qualifiers unnecessary. + + When we say that a module _exports_ a declaration, we mean that the + declaration is _public_. For more details about name spaces and export + statements, see the section on "Exports" in the + [Dart Language Specification](https://www.dartlang.org/docs/spec/). - The `HeroDetailComponent` is a *different* component than the `HeroListComponent` we've seen. - The `HeroDetailComponent` (code not shown) presents facts about a particular hero, the - hero that the user selects from the list presented by the `HeroListComponent`. - The `HeroDetailComponent` is a **child** of the `HeroListComponent`. +block ts-import + //- N/A -figure - img(src="/resources/images/devguide/architecture/component-tree.png" alt="Metadata" align="left" style="width:300px; margin-left:-40px;margin-right:10px" ) -:marked - Notice how `` rests comfortably among native HTML elements. - We can and _will_ mix our custom components with native HTML in the same layouts. - - In this manner we'll compose complex component trees to build out our richly featured application. -
    - -.l-main-section - -:marked - ## Metadata -figure - img(src="/resources/images/devguide/architecture/metadata.png" alt="Metadata" align="left" style="width:150px; margin-left:-40px;margin-right:10px" ) -:marked -

    Metadata tells Angular how to process a class.

    -
    -:marked - [Looking back at the code](#component-code) for `HeroListComponent`, we see that it's just a class. - There is no evidence of a framework, no "Angular" in it at all. - - In fact, it really is *just a class*. It's not a component until we *tell Angular about it*. - - We tell Angular that `HeroListComponent` is a component by attaching **metadata** to the class. - - In Dart, we attach metadata by using an **annotation**. - Here's some metadata for `HeroListComponent`: - -+makeExample('architecture/dart/lib/hero_list_component.dart', 'metadata', 'lib/hero_list_component.dart') -:marked - Here we see the `@Component` annotation, which (no surprise) identifies the class - immediately below it as a component class. - - Annotations often have configuration parameters. - The `@Component` annotation takes parameters to provide the - information Angular needs to create and present the component and its view. - - Here we see a few of the possible `@Component` parameters: - - * `selector`: A CSS selector that tells Angular to create and insert an instance of this component - where it finds a `` tag in *parent* HTML. - For example, if an app's HTML contains ``, then - Angular inserts an instance of the `HeroListComponent` view between those tags. - - * `templateUrl`: The address of this component's template, which we showed [above](#the-template). - - * `directives`: An array of the components or directives that *this* template requires. - We saw in the last line of our template that we expect Angular to insert a `HeroDetailComponent` - in the space indicated by `` tags. - Angular will do so only if we mention the `HeroDetailComponent` in this `directives` array. - - * `providers`: An array of **dependency injection providers** for services that the component requires. - This is one way to tell Angular that our component's constructor requires a `HeroService` - so it can get the list of heroes to display. We'll get to dependency injection later. -figure - img(src="/resources/images/devguide/architecture/template-metadata-component.png" alt="Metadata" align="left" style="height:200px; margin-left:-40px;margin-right:10px" ) - -:marked - At runtime, Angular discovers the metadata specified by the `@Component` - annotation. That's how Angular learns how to do "the right thing". - - The template, metadata, and component together describe the view. - - We apply other metadata annotations in a similar fashion to guide Angular behavior. - `@Injectable`, `@Input`, `@Output`, and `@RouterConfig` are a few of the more popular annotations - we'll master as our Angular knowledge grows. -
    -:marked - The architectural takeaway is that we must add metadata to our code - so that Angular knows what to do. - -.l-main-section - -:marked - ## Data binding - Without a framework, we would be responsible for pushing data values into the HTML controls and turning user responses - into actions and value updates. Writing such push/pull logic by hand is tedious, error-prone, and a nightmare to - read as any experienced jQuery programmer can attest. -figure - img(src="/resources/images/devguide/architecture/databinding.png" alt="Data Binding" style="width:220px; float:left; margin-left:-40px;margin-right:20px" ) -:marked - Angular supports **data binding**, - a mechanism for coordinating parts of a template with parts of a component. - We add binding markup to the template HTML to tell Angular how to connect both sides. - - There are four forms of data binding syntax. Each form has a direction — to the DOM, from the DOM, or in both directions — - as indicated by the arrows in the diagram. -
    -:marked - We saw three forms of data binding in our [example](#template) template: -+makeExample('architecture/dart/lib/hero_list_component_1.html', 'binding')(format=".") -:marked - * The {‌{hero.name}} [interpolation](displaying-data.html#interpolation) - displays the component's `hero.name` property value within the `
    ` tags. - - * The `[hero]` property binding - passes the value of `selectedHero` from - the parent `HeroListComponent` to the `hero` property of the child `HeroDetailComponent`. - - * The `(click)` [event binding](user-input.html#click) calls the component's `selectHero` method when the user clicks a hero's name. - - **Two-way data binding** is an important fourth form - that combines property and event binding in a single notation, using the `ngModel` directive. - We didn't have a two-way binding in the `HeroListComponent` template; - here's an example from the `HeroDetailComponent` template: - -+makeExample('architecture/dart/lib/hero_detail_component.html', 'ng-model', 'lib/hero_detail_component.html (excerpt)')(format=".") -:marked - In two-way binding, a data property value flows to the input box from the component as with property binding. - The user's changes also flow back to the component, resetting the property to the latest value, - as with event binding. - - Angular processes *all* data bindings once per JavaScript event cycle, - depth-first from the root of the application component tree. - -figure - img(src="/resources/images/devguide/architecture/component-databinding.png" alt="Data Binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px" ) -:marked - We don't know all the details yet, - but it's clear from these examples that data binding plays an important role in communication - between a template and its component. -
    -figure - img(src="/resources/images/devguide/architecture/parent-child-binding.png" alt="Parent/Child binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px" ) -:marked - Data binding is also important for communication between parent and child components. -
    - -.l-main-section - -:marked - ## Directives -figure - img(src="/resources/images/devguide/architecture/directive.png" alt="Parent child" style="float:left; width:150px; margin-left:-40px;margin-right:10px" ) -:marked - Angular templates are *dynamic*. When Angular renders them, it transforms the DOM - according to the instructions given by **directives**. - - A directive is a class with directive metadata. In Dart we apply the `@Directive` annotation - to attach metadata to the class. -
    -:marked - We already met one form of directive: the component. A component is a *directive-with-a-template*; - a `@Component` annotation is actually a `@Directive` annotation extended with template-oriented features. - -.l-sub-section +block angular-library-modules :marked - While **a component is technically a directive**, - components are so distinctive and central to Angular applications that we chose - to separate components from directives in this architectural overview. -:marked - Two *other* kinds of directives exist: _structural_ and _attribute_ directives. + Angular ships as a collection of libraries within the + [**angular2**](https://pub.dartlang.org/packages/angular2) package. - They tend to appear within an element tag like attributes, - sometimes by name but more often as the target of an assignment or a binding. +block angular-imports + +makeExcerpt('app/app.component.ts', 'import') - **Structural** directives alter layout by adding, removing, and replacing elements in DOM. - - Our [example](#template) template uses two built-in structural directives: -+makeExample('architecture/dart/lib/hero_list_component_1.html', 'structural')(format=".") -:marked - * [`*ngFor`](displaying-data.html#ng-for) tells Angular to stamp out one `
    ` per hero in the `heroes` list. - * [`*ngIf`](displaying-data.html#ng-if) includes the `HeroDetail` component only if a selected hero exists. - -.l-sub-section +block ts-decorator :marked - In Dart, **the only value that is true is the boolean value `true`**; all - other values are false. JavaScript and TypeScript, in contrast, treat values - such as 1 and most non-null objects as true. For this reason, the JavaScript - and TypeScript versions of this app can use just `selectedHero` as the value - of the `*ngIf` expression. The Dart version must use a boolean operator such - as `!=` instead. + Annotations often have configuration parameters. + The `@Component` annotation takes parameters to provide the + information Angular needs to create and present the component and its view. -:marked - **Attribute** directives alter the appearance or behavior of an existing element. - In templates they look like regular HTML attributes, hence the name. + Here are a few of the possible `@Component` parameters: - The `ngModel` directive, which implements two-way data binding, is - an example of an attribute directive. `ngModel` modifies the behavior of - an existing element (typically an ``) - by setting its display value property and responding to change events. - -+makeExample('architecture/dart/lib/hero_detail_component.html', 'ng-model')(format=".") -:marked - Angular ships with a small number of other directives that either alter the layout structure - (for example, `ngSwitch`) - or modify aspects of DOM elements and components - (for example, `ngStyle` and `ngClass`). - - - Of course, we can also write our own directives. Components such as - `HeroListComponent` are one kind of custom directive. - - -.l-main-section - -:marked - ## Services -figure - img(src="/resources/images/devguide/architecture/service.png" alt="Service" style="float:left; margin-left:-40px;margin-right:10px" ) -:marked - _Services_ is a broad category encompassing any value, function, or feature that our application needs. - - Almost anything can be a service. - A service is typically a class with a narrow, well-defined purpose. It should do something specific and do it well. -
    -:marked - Examples include: - * logging service - * data service - * message bus - * tax calculator - * application configuration - - There is nothing specifically _Angular_ about services. Angular itself has no definition of a service. - There is no service base class, and no place to register a service. - - Yet services are fundamental to any Angular application. Our components are big consumers of services. - - We prefer our component classes lean. Our components don't fetch data from the server, - they don't validate user input, and they don't log directly to console. They delegate such tasks to services. - - A component's job is to enable the user experience and nothing more. It mediates between the view (rendered by the template) - and the application logic (which often includes some notion of a _model_). - A good component presents properties and methods for data binding. - It delegates everything nontrivial to services. - - Angular doesn't *enforce* these principles. - It won't complain if we write a "kitchen sink" component with 3000 lines. - - Angular does help us *follow* these principles by making it easy to factor our - application logic into services and make those services available to components through *dependency injection*. - -.l-main-section - -:marked - ## Dependency injection -figure - img(src="/resources/images/devguide/architecture/dependency-injection.png" alt="Service" style="float:left; width:200px; margin-left:-40px;margin-right:10px" ) -:marked - Dependency injection is a way to supply a new instance of a class - with the fully-formed dependencies it requires. Most dependencies are services. - Angular uses dependency injection to provide new components with the services they need. -
    -:marked - Angular can tell which services a component needs by looking at the types of its constructor parameters. - For example, the constructor of our `HeroListComponent` needs a `HeroService`: -+makeExample('architecture/dart/lib/hero_list_component.dart', 'ctor', 'lib/hero_list_component.dart (excerpt)')(format='.') -:marked - When Angular creates a component, it first asks an **injector** for - the services that the component requires. - - An injector maintains a container of service instances that it has previously created. - If a requested service instance is not in the container, the injector makes one and adds it to the container - before returning the service to Angular. - When all requested services have been resolved and returned, - Angular can call the component's constructor with those services as arguments. - This is what we mean by *dependency injection*. - - The process of `HeroService` injection looks a bit like this: -figure - img(src="/resources/images/devguide/architecture/injector-injects.png" alt="Service" ) -:marked - If the injector doesn't have a `HeroService`, how does it know how to make one? - - In brief, we must have previously registered a **provider** of the `HeroService` with the injector. - A provider is something that can create or return a service, typically the service class itself. - - We can register providers at any level of the application component tree. - We often do so at the root when we bootstrap the application so that - the same instance of a service is available everywhere. -+makeExample('architecture/dart/web/main.dart', 'bootstrap', 'web/main.dart (excerpt)')(format='.') -:marked - Alternatively, we might register at a component level: -+makeExample('architecture/dart/lib/hero_list_component.dart', 'providers', 'lib/hero_list_component.dart (excerpt)')(format='.') -:marked - Registering at a component level means we get a new instance of the - service with each new instance of that component. - - - - Points to remember about dependency injection: - - * Dependency injection is wired into the Angular framework and used everywhere. - - * The *injector* is the main mechanism. - * An injector maintains a *container* of service instances that it created. - * An injector can create a new service instance from a *provider*. - - * A *provider* is a recipe for creating a service. - - * We register *providers* with injectors. - - -.l-main-section -:marked - ## Other stuff - - We've learned just a bit about the seven main building blocks of an Angular application: - 1. [Components](#component) - 1. [Templates](#template) - 1. [Metadata](#metadata) - 1. [Data binding](#data-binding) - 1. [Directives](#directive) - 1. [Services](#service) - 1. [Dependency injection](#dependency-injection) - - That's a foundation for everything else in an Angular application, - and it's more than enough to get going. - But it doesn't include everything we'll need or want to know. - - Here is a brief, alphabetical list of other important Angular features and services. - Most of them are covered in this Developers Guide (or soon will be). - - >**Animations:** A forthcoming animation library makes it easy for developers to animate component behavior - without deep knowledge of animation techniques or CSS. - - >**Bootstrap:** A method to configure and launch the root application component. - - >**Change detection:** Learn how Angular decides that a component property value has changed and - when to update the screen. - Learn how it uses **zones** to intercept asynchronous activity and run its change detection strategies. - - >**Component router:** With the component Router service, users can navigate a multi-screen application - in a familiar web browsing style using URLs. - - >**Events:** The DOM raises events. So can components and services. Angular offers mechanisms for - publishing and subscribing to events including an implementation of the [RxJS Observable](https://github.com/zenparsing/es-observable) proposal. - - >**[Forms](forms.html):** Support complex data entry scenarios with HTML-based validation and dirty checking. - - >**HTTP:** Communicate with a server to get data, save data, and invoke server-side actions with this Angular HTTP client. - - >**Lifecycle hooks:** We can tap into key moments in the lifetime of a component, from its creation to its destruction, - by implementing the lifecycle hook interfaces. - - >**Pipes:** Services that transform values for display. - We can put pipes in our templates to improve the user experience. Consider - this `currency` pipe expression: -
    -code-example(language="javascript" linenumbers="."). - price | currency:'USD':true' -
    -:marked - >It displays a price of "42.33" as `$42.33`. - - >**Testing:** Angular provides a - [testing library](https://pub.dartlang.org/packages/angular2_testing) - to run unit tests on our application parts as they interact with the Angular framework. +block dart-bool + .callout.is-helpful + header Dart difference: Only true is true + :marked + In Dart, **the only value that is true is the boolean value `true`**; all + other values are false. JavaScript and TypeScript, in contrast, treat values + such as 1 and most non-null objects as true. For this reason, the JavaScript + and TypeScript versions of this app can use just `selectedHero` as the value + of the `*ngIf` expression. The Dart version must use a boolean operator such + as `!=` instead. diff --git a/public/docs/dart/latest/guide/component-styles.jade b/public/docs/dart/latest/guide/component-styles.jade index 71507a7d01..967dc2d8b0 100644 --- a/public/docs/dart/latest/guide/component-styles.jade +++ b/public/docs/dart/latest/guide/component-styles.jade @@ -19,7 +19,7 @@ block css-import-url .alert.is-important :marked URLs are currently not interpreted in this way, see - [issue 8518](href="https://github.com/angular/angular/issues/8518"). + [issue 8518](https://github.com/angular/angular/issues/8518). Until this issue is fixed, absolute package-reference style URLs must be given as is illustrated below. diff --git a/public/docs/dart/latest/guide/dependency-injection.jade b/public/docs/dart/latest/guide/dependency-injection.jade index ad695a9732..7236d24c7d 100644 --- a/public/docs/dart/latest/guide/dependency-injection.jade +++ b/public/docs/dart/latest/guide/dependency-injection.jade @@ -101,7 +101,7 @@ block dart-map-alternative As an alternative to using a configuration `Map`, we can define a custom configuration class: - +makeExample('dependency-injection/ts/app/app.config.ts','config-alt','app/app-config.ts (alternative config)')(format='.') + +makeExample('lib/app_config.dart (alternative config)','config-alt') :marked Defining a configuration class has a few benefits. One key benefit diff --git a/public/docs/dart/latest/guide/forms-deprecated.html b/public/docs/dart/latest/guide/forms-deprecated.html new file mode 100644 index 0000000000..f8df2a84a6 --- /dev/null +++ b/public/docs/dart/latest/guide/forms-deprecated.html @@ -0,0 +1 @@ +!= partial("../../../_includes/_ts-temp") \ No newline at end of file diff --git a/public/docs/dart/latest/guide/forms.jade b/public/docs/dart/latest/guide/forms.jade index e78d9c19eb..19b7d09aa5 100644 --- a/public/docs/dart/latest/guide/forms.jade +++ b/public/docs/dart/latest/guide/forms.jade @@ -517,7 +517,7 @@ figure.image-display Recall from the previous section that `ngControl` registered this input box with the `NgForm` directive as "name". - We didn't add the **[`NgForm`](../api/common/NgForm-directive.html) directive* + We didn't add the **[`NgForm`](../api/common/index/NgForm-directive.html) directive* explicitly. Angular added it surreptitiously, wrapping it around the `
    ` element. The `NgForm` directive supplements the `` element with additional features. diff --git a/public/docs/dart/latest/guide/glossary.jade b/public/docs/dart/latest/guide/glossary.jade index a66284569e..ca1429104e 100644 --- a/public/docs/dart/latest/guide/glossary.jade +++ b/public/docs/dart/latest/guide/glossary.jade @@ -1 +1 @@ -!= partial("../glossary") +include ../glossary diff --git a/public/docs/dart/latest/guide/server-communication.jade b/public/docs/dart/latest/guide/server-communication.jade index 1c3ad3155c..4064b7ec97 100644 --- a/public/docs/dart/latest/guide/server-communication.jade +++ b/public/docs/dart/latest/guide/server-communication.jade @@ -33,7 +33,7 @@ block http-providers block getheroes-and-addhero :marked The hero service `getHeroes()` and `addHero()` asynchronous methods return the - [`Future`](https://api.dartlang.org/stable/1.16.0/dart-async/Future-class.html) + [`Future`](https://api.dartlang.org/stable/dart-async/Future-class.html) values of the current hero list and the newly added hero, respectively. The hero list component methods of the same name specifying the actions to be taken when the asynchronous method calls succeed or fail. diff --git a/public/docs/dart/latest/guide/template-syntax.jade b/public/docs/dart/latest/guide/template-syntax.jade index 8ba4989112..71878e7b94 100644 --- a/public/docs/dart/latest/guide/template-syntax.jade +++ b/public/docs/dart/latest/guide/template-syntax.jade @@ -5,7 +5,7 @@ block includes - var _JavaScript = 'Dart'; - var __chaining_op = ';'; - var __new_op = 'new or const'; - - var mapApiRef = 'https://api.dartlang.org/stable/1.16.0/dart-core/Map-class.html'; + - var mapApiRef = 'https://api.dartlang.org/stable/dart-core/Map-class.html'; - var __objectAsMap = 'Map' block notable-differences @@ -69,7 +69,7 @@ block style-property-name-dart-diff and `setProperty()`. Hence, we recommend only using dash-case for style property names. - [CssSD]: https://api.dartlang.org/stable/1.16.1/dart-html/CssStyleDeclaration-class.html + [CssSD]: https://api.dartlang.org/stable/dart-html/CssStyleDeclaration-class.html block dart-no-truthy-falsey diff --git a/public/docs/dart/latest/quickstart.jade b/public/docs/dart/latest/quickstart.jade index 7352d90826..e2a026d4f7 100644 --- a/public/docs/dart/latest/quickstart.jade +++ b/public/docs/dart/latest/quickstart.jade @@ -17,8 +17,8 @@ block setup-tooling You can also download [Dart plugins for other IDEs and editors][DT]. [WS]: https://confluence.jetbrains.com/display/WI/Getting+started+with+Dart - [DT]: https://www.dartlang.org/tools - [pub]: https://www.dartlang.org/tools/pub + [DT]: https://www.dartlang.org/tools/ + [pub]: https://www.dartlang.org/tools/pub/ block download-source // exclude this section from Dart diff --git a/public/docs/dart/latest/tutorial/toh-pt5.jade b/public/docs/dart/latest/tutorial/toh-pt5.jade index 1c0ab90de8..ae8c6ef823 100644 --- a/public/docs/dart/latest/tutorial/toh-pt5.jade +++ b/public/docs/dart/latest/tutorial/toh-pt5.jade @@ -132,9 +132,6 @@ code-example(language="bash"). ### Add a base tag -// Our Tour of Heroes needs routing, -// so we load the library in the `index.html` in a script tag immediately *after* the angular script itself. -//+makeExample('toh-5/dart/web/index.html', 'router', 'index.html (router)')(format=".") :marked First, edit `index.html` and add `` at the top of the `` section. +makeExample('toh-5/dart/web/index.html', 'base-href', 'index.html (base href)')(format=".") @@ -442,7 +439,7 @@ code-example(format=''). :marked Going back too far could take us out of the application. That's acceptable in a demo. We'd guard against it in a real application, - perhaps with the [*routerCanDeactivate* hook](../api/router/CanDeactivate-interface.html). + perhaps with the [*routerCanDeactivate* hook](../api/router/index/CanDeactivate-interface.html). :marked Then we wire this method with an event binding to a *Back* button that we add to the bottom of the component template. +makeExample('toh-5/dart/lib/hero_detail_component.html', 'back-button')(format=".") diff --git a/public/docs/js/latest/cookbook/dynamic-form-deprecated.html b/public/docs/js/latest/cookbook/dynamic-form-deprecated.html new file mode 100644 index 0000000000..f8df2a84a6 --- /dev/null +++ b/public/docs/js/latest/cookbook/dynamic-form-deprecated.html @@ -0,0 +1 @@ +!= partial("../../../_includes/_ts-temp") \ No newline at end of file diff --git a/public/docs/js/latest/guide/animations.jade b/public/docs/js/latest/guide/animations.jade new file mode 100644 index 0000000000..6778b6af28 --- /dev/null +++ b/public/docs/js/latest/guide/animations.jade @@ -0,0 +1 @@ +!= partial("../../../_includes/_ts-temp") diff --git a/public/docs/js/latest/guide/forms-deprecated.jade b/public/docs/js/latest/guide/forms-deprecated.jade index 185b25dee5..e17b9fe46c 100644 --- a/public/docs/js/latest/guide/forms-deprecated.jade +++ b/public/docs/js/latest/guide/forms-deprecated.jade @@ -493,7 +493,7 @@ figure.image-display :marked ### The NgForm directive We just set a template local variable with the value of an `NgForm` directive. - Why did that work? We didn't add the **[`NgForm`](../api/common/NgForm-directive.html) directive** explicitly. + Why did that work? We didn't add the **[`NgForm`](../api/common/index/NgForm-directive.html) directive** explicitly. Angular added it surreptitiously, wrapping it around the `` element diff --git a/public/docs/js/latest/guide/forms.jade b/public/docs/js/latest/guide/forms.jade index a02c2a53b3..2613b7b6b7 100644 --- a/public/docs/js/latest/guide/forms.jade +++ b/public/docs/js/latest/guide/forms.jade @@ -491,7 +491,7 @@ figure.image-display .l-sub-section :marked Why "ngModel"? - A directive's [exportAs](../api/core/DirectiveMetadata-class.html#!#exportAs) property + A directive's [exportAs](../api/core/index/DirectiveMetadata-class.html#!#exportAs) property tells Angular how to link the reference variable to the directive. We set `name` to `ngModel` because the `ngModel` directive's `exportAs` property happens to be "ngModel". @@ -503,7 +503,7 @@ figure.image-display :marked ### The NgForm directive We just set a template local variable with the value of an `NgForm` directive. - Why did that work? We didn't add the **[`NgForm`](../api/common/NgForm-directive.html) directive** explicitly. + Why did that work? We didn't add the **[`NgForm`](../api/common/index/NgForm-directive.html) directive** explicitly. Angular added it surreptitiously, wrapping it around the `` element diff --git a/public/docs/ts/latest/cookbook/a1-a2-quick-reference.jade b/public/docs/ts/latest/cookbook/a1-a2-quick-reference.jade index 94d997b8bb..5a0711b72f 100644 --- a/public/docs/ts/latest/cookbook/a1-a2-quick-reference.jade +++ b/public/docs/ts/latest/cookbook/a1-a2-quick-reference.jade @@ -227,7 +227,7 @@ table(width="100%") ### 引导 - +makeExample('cb-a1-a2-quick-reference/ts/app/main.ts')(format="." ) + +makeExample('cb-a1-a2-quick-reference/ts/app/main.1.ts')(format="." ) :marked Angular 2 does not have a bootstrap directive. We always launch the app in code by explicitly calling a bootstrap function diff --git a/public/docs/ts/latest/cookbook/set-document-title.jade b/public/docs/ts/latest/cookbook/set-document-title.jade index 77effaba07..e71d6739a7 100644 --- a/public/docs/ts/latest/cookbook/set-document-title.jade +++ b/public/docs/ts/latest/cookbook/set-document-title.jade @@ -58,7 +58,7 @@ code-example(format='') ## 使用*Title*服务 Fortunately, Angular 2 bridges the gap by providing a `Title` service as part of the *Browser platform*. - The [Title](../api/platform/browser/Title-class.html) service is a simple class that provides an API + The [Title](../api/platform-browser/index/Title-class.html) service is a simple class that provides an API for getting and setting the current HTML document title: 幸运的是,Angular 2在*浏览器平台*的包中,提供了一个`Title`服务,弥补了这个差别。 diff --git a/public/docs/ts/latest/glossary.jade b/public/docs/ts/latest/glossary.jade index 043dea46f1..d81dea7844 100644 --- a/public/docs/ts/latest/glossary.jade +++ b/public/docs/ts/latest/glossary.jade @@ -73,7 +73,8 @@ include _util-fns Imagine three modules in a `heroes` folder: 设想在`heroes`目录下有三个模块: - code-example(format=''). + + code-example. // heroes/hero.component.ts export class HeroComponent {} @@ -86,7 +87,8 @@ include _util-fns Without a barrel, a consumer would need three import statements: 假如没有封装桶,消费者就需要三条import语句: - code-example(format=''). + + code-example. import { HeroComponent } from '../heroes/hero.component.ts'; import { Hero } from '../heroes/hero.model.ts'; import { HeroService } from '../heroes/hero.service.ts'; @@ -94,7 +96,8 @@ include _util-fns We can add a barrel to the `heroes` folder (called `index` by convention) that exports all of these items: 在`heroes`目录下添加一个封装桶(按规约叫做`index`),它导出所有这三条: - code-example(format=''). + + code-example. export * from './hero.model.ts'; // re-export all of its exports export * from './hero.service.ts'; // re-export all of its exports export { HeroComponent } from './hero.component.ts'; // re-export the named thing @@ -102,7 +105,8 @@ include _util-fns Now a consumer can import what it needs from the barrel. 现在,消费者就就可以从这个封装桶中导入它需要的东西了。 - code-example(format=''). + + code-example. import { Hero, HeroService } from '../heroes'; // index is implied :marked The Angular [scoped packages](#scoped-package) each have a barrel named `index`. @@ -113,7 +117,8 @@ include _util-fns That's why we can write this: 这就是为什么可以这样写的原因: -+makeExample('../docs/_fragments/quickstart/ts/app/app.component.ts', 'import')(format=".") + ++makeExcerpt('quickstart/ts/app/app.component.ts', 'import', '') // #docregion b-c :marked @@ -862,8 +867,8 @@ include _util-fns 使用和导入*普通*包相同的方式导入范围化包。 从消费者的视角看,唯一的不同是那些包的名字是用Angular的*范围名*`@angular`开头儿的。 -+makeExample('../docs/_fragments/architecture/ts/app/app.component.ts', 'import')(format=".") -// #docregion n-s + +makeExcerpt('architecture/ts/app/app.component.ts', 'import', '') +// #docregion n-s-2 :marked ## Structural Directive diff --git a/public/docs/ts/latest/guide/architecture.jade b/public/docs/ts/latest/guide/architecture.jade index 2f6d27088c..5aeb7e7c46 100644 --- a/public/docs/ts/latest/guide/architecture.jade +++ b/public/docs/ts/latest/guide/architecture.jade @@ -1,49 +1,56 @@ -include ../_util-fns +block includes + include ../_util-fns + - var _library_module = 'library module' + - var _at_angular = '@angular' :marked - Angular 2 is a framework to help us build client applications in HTML and JavaScript. + Angular 2 is a framework to help us build client applications in HTML and + either JavaScript or a language (like Dart or TypeScript) that compiles to JavaScript. - Angular 2是一个用HTML和JavaScript构建客户端应用的框架。 - - The framework consists of several cooperating libraries, some of them core and some optional. - - 该框架包括一系列紧密合作的库,有些是核心库,有些是可选库。 - - We write applications by composing HTML *templates* with Angularized-markup, + Angular 2是一个用HTML和JavaScript或者一个可以编译成JavaScript的语言(比如Dart或者TypeScript),来构建客户端应用的框架。 + +block angular-parts + :marked + The framework consists of several cooperating libraries, some of them core and some optional. + + 该框架包括一系列紧密合作的库,有些是核心库,有些是可选库。 + +:marked + With Angular, we write applications by composing HTML *templates* with Angularized-markup, writing *component* classes to manage those templates, adding application logic in *services*, and handing the top root component to Angular's *bootstrapper*. - 我们这样写应用程序:用带Angular扩展语法的HTML写*模板*,用*组件*类管理这些模板,用*服务*添加应用逻辑,用根组件完成Angular*启动*。 + 在Angular里,我们这样写应用程序:用带Angular扩展语法的HTML写*模板*,用*组件*类管理这些模板,用*服务*添加应用逻辑,用根组件完成Angular*启动*。 Angular takes over, presenting our application content in a browser and responding to user interactions according to the instructions we provided. Angular在浏览器中接管、展现应用的内容,并根据我们提供的操作指令响应用户的交互。 - - -:marked - Of course there is more to it than this. We'll learn the details when we dive into the guide chapters. + + Of course there is more to it than this. + We'll learn the details when we dive into the guide chapters. Let's get the big picture first. - + 当然,这只是冰山一角。随着内容的深入,我们还会学到更多的细节。 我们先来看看宏观图景。 figure img(src="/resources/images/devguide/architecture/overview2.png" alt="overview" style="margin-left:-40px;" width="700") + :marked The architecture diagram identifies the eight main building blocks of an Angular 2 application: 这个架构图展现了Angular应用中的8个主要构造块: - 1. [Module](#module) + 1. [Modules](#modules) 1. [模块(Module)](#module) - 1. [Component](#component) + 1. [Components](#components) 1. [组件(Component)](#component) - 1. [Template](#template) + 1. [Templates](#templates) 1. [模板(Template)](#template) @@ -66,22 +73,23 @@ figure 1. [Dependency Injection](#dependency-injection) 1. [依赖注入(Dependency Injection)](#dependency-injection) - - Learn these eight and we're on our way. - 掌握这8点,我们就可以开始使用Angular 2编写应用程序了。 + Learn these, and we're on our way. + + 掌握这些后,我们就可以开始使用Angular 2编写应用程序了。 + .l-sub-section - :marked - The code referenced in this chapter is available as a [live example](/resources/live-examples/architecture/ts/plnkr.html). + p The code referenced in this chapter is available as a #[+liveExampleLink2()]. + + p 本章所引用的代码可以从这个#[+liveExampleLink2('在线例子')]。 - 本章所引用的代码可以从这个[在线例子](/resources/live-examples/architecture/ts/plnkr.html)中找到。 - - .l-main-section :marked - ## The Module + ## Modules + ## 模块 + figure img(src="/resources/images/devguide/architecture/module.png" alt="模块" align="left" style="width:240px; margin-left:-40px;margin-right:10px" ) :marked @@ -98,11 +106,19 @@ figure 典型的模块是一个内聚的代码块,用以实现单一的目的。 在这些代码中,模块会*导出*一些东西,最典型的就是类。 +

    + +block modules-in-dart + //- N/A + +block modules-are-optional .l-sub-section :marked ### Modules are optional + ### 模块是可选的 + We highly recommend modular design. TypeScript has great support for ES2015 module syntax and our chapters assume we're taking a modular approach using that syntax. That's why we list *Module* among the basic building blocks. @@ -119,8 +135,10 @@ figure 在JavaScript(你可以从页面顶部的组合框选择JavaScript)分支下,可以找到如何安装和组织的线索。 它示范了如何用老版本的JavaScript语言,在没有模块化系统支持的情况下进行Angular 2应用程序的开发。 + +- var _app_comp_filename = _docsFor == 'dart' ? 'app_component.dart' : 'app.component.ts'; :marked - Perhaps the first module we meet is a module that exports a *component* class. + Perhaps the first module we meet is a module that exports a *component* class. The component is one of the basic Angular blocks, we write a lot of them, and we'll talk about components in the next segment. For the moment it is enough to know that a component class is the kind of thing we'd export from a module. @@ -135,65 +153,80 @@ figure 大多数应用都有一个`AppComponent`。按照惯例,它位于一个名叫`app.component.ts`的文件中。 打开它,我们将会看到一个`export`语句,就像这样: -+makeExample('architecture/ts/app/app.component.ts', 'export', 'app/app.component.ts (节选)')(format=".") ++makeExcerpt('app/app.component.ts ()', 'export') + +block export-qualifier :marked The `export` statement tells TypeScript that this is a module whose `AppComponent` class is public and accessible to other modules of the application. `export`语句告诉TypeScript:这是一个模块,其中`AppComponent`类是公开的,可以被应用程序中的其它模块访问。 - +:marked When we need a reference to the `AppComponent`, we **import** it like this: 当需要引用`AppComponent`时,要像这样**导入**它: -+makeExample('architecture/ts/app/main.ts', 'import', 'app/main.ts (节选)')(format=".") ++makeExcerpt('app/main.ts', 'import') + +block ts-import + :marked + The `import` statement tells the system it can get an `AppComponent` from a module named `app.component` + located in a neighboring file. + The **module name** (AKA module id) is often the same as the filename without its extension. + + `import`语句告诉系统,它能从附近名叫`app.component`的文件中获得`AppComponent`组件。 + **模块名**(又叫模块ID)通常和去掉扩展名后的文件名相同。 + :marked - The `import` statement tells the system it can get an `AppComponent` from a module named `app.component` - located in a neighboring file. - The **module name** (AKA module id) is often the same as the filename without its extension. - - `import`语句告诉系统,它能从附近名叫`app.component`的文件中获得`AppComponent`组件。 - **模块名**(又叫模块ID)通常和去掉扩展名后的文件名相同。 - - ### Library Modules + ### Libraries + ### 模块库 figure img(src="/resources/images/devguide/architecture/library-module.png" alt="组件" align="left" style="width:240px; margin-left:-40px;margin-right:10px" ) + +block angular-library-modules + :marked + Some modules are _libraries_ of other modules. + + 有些模块是其它模块的库。 + + Angular itself ships as a collection of library modules within several npm packages. + Their names begin with the `!{_at_angular}` prefix. + + Angular本身就是通过npm包发布的一组模块库,它们都以`@angular`为前缀。 + + Each Angular library contains a [barrel](../glossary.html#barrel) module + that is actually a public façade over several logically-related private modules. + + 每个Angular库中都包含一个[封装桶](../glossary.html#barrel)模块。 + 它实际上是一个公开的外观层(façade),囊括了一些逻辑相关的私有模块。 + :marked - Some modules are libraries of other modules. - - 有些模块是其它模块的库。 - - Angular itself ships as a collection of library modules within several npm packages. - Their names begin with the `@angular` prefix. - Each Angular library contains a [barrel](../glossary.html#barrel) module - that is actually a public façade over several logically-related private modules. - - Angular本身就是通过npm包发布的一组模块库,它们都以`@angular`为前缀。 - 每个Angular库中都包含一个[封装桶](../glossary.html#barrel)模块。 - 它实际上是一个公开的外观层(façade),囊括了一些逻辑相关的私有模块。 - - The `@angular/core` library is the primary Angular library module from which we get most of what we need. + `!{_at_angular}/core` is the primary Angular library from which we get most of what we need. `@angular/core`库是主要的Angular模块库,从这里能获得大部分需要的东西。 +
    - There are other important Angular library modules too such as `@angular/common`, `@angular/router`, and `@angular/http`. + There are other important Angular libraries too, such as `!{_at_angular}/common`, `!{_at_angular}/router`, and `!{_at_angular}/animate`. + We import what we need from an Angular !{_library_module}. - 还有另一些重要的Angular模块库,比如`@angular/common`、`@angular/router` 和 `@angular/http`。 + 还有另一些重要的Angular模块库,比如`@angular/common`、`@angular/router` 和 `@angular/animate`。我们从一个Angular的!{_library_module}导入我们需要的模块。 - We import what we need from an Angular library module in much the same way. - For example, we import the Angular **`Component` *function*** from the *@angular/core* module like this: +block angular-imports + :marked + For example, we import the Angular **`Component` *function*** from the *@angular/core* module like this: - 从Angular模块库中导入所需内容的方式都差不多。 - 比如,从*@angular/core*中导入Angular **`Component`*函数***的代码是这样的: + 比如,从*@angular/core*中导入Angular **`Component`*函数***的代码是这样的: -+makeExample('architecture/ts/app/app.component.ts', 'import')(format=".") -:marked - Compare that syntax to our previous import of `AppComponent`. + +makeExcerpt('app/app.component.ts', 'import') + :marked + Compare that syntax to our previous import of `AppComponent`. + + 比较一下它和前面导入`AppComponent`时的语法。 + + +makeExcerpt('app/main.ts', 'import') - 比较一下它和前面导入`AppComponent`时的语法。 -+makeExample('architecture/ts/app/main.ts', 'import')(format=".") :marked Notice the difference? In the first case, when importing from an Angular library module, @@ -210,6 +243,7 @@ figure 当我们从*自己的*文件中导入时,模块名中带有路径前缀。 在这个例子中,前缀是一个相对路径(./)。这表示源模块和想导入它的模块位于同一个目录中(./)。 如果源模块位于其它位置,我们还可以向上引用应用目录结构中的任意路径(如`../../../somewhere/`)。 + .l-sub-section :marked We import and export in the ECMAScript 2015 (ES2015) module syntax. @@ -226,7 +260,9 @@ figure “模块加载与导入”背后的基础设施是一个很重要的话题,但它不在Angular简介的范围内。 目前的焦点是我们的应用程序,只需要知道*import*和*export*就够了。 - + +- var _export = _docsFor == 'dart' ? 'publicly declare' : 'export'; +- var _declare = _docsFor == 'dart' ? 'declare' : 'export'; :marked The key take-aways are: @@ -236,7 +272,7 @@ figure * Angular应用是由模块组成的。 - * Modules export things — classes, function, values — that other modules import. + * Modules !{_export} things — classes, function, values — that other modules import. * 模块导出一些东西 —— 类,函数,值,供其它模块导入。 @@ -244,93 +280,90 @@ figure * 首选的写法是把应用写成一组模块,每个模块只导出一样东西。 - The first module we write will most likely export a component. + The first module we write will most likely !{_declare} a component. 我们写的第一个模块将会导出一个组件。 .l-main-section - :marked - ## The Component + ## Components + ## 组件 + figure img(src="/resources/images/devguide/architecture/hero-component.png" alt="组件" align="left" style="width:200px; margin-left:-40px;margin-right:10px" ) + :marked - A **Component** controls a patch of screen real estate that we could call a *view*. + A **component** controls a patch of screen real estate that we could call a *view*. The shell at the application root with navigation links, that list of heroes, the hero editor ... - they're all views controlled by Components. + they're all views controlled by components. **组件**控制屏幕中巴掌大的那么一小块地方,这块地方被称之为*视图*。 应用的“外壳”包括一些导航链接、英雄列表、英雄编辑器…… 它们都是由组件控制的视图。 - We define a Component's application logic - what it does to support the view - inside a class. - The class interacts with the view through an API of properties and methods. + We define a component's application logic — what it does to support the view — inside a class. + The class interacts with the view through an API of properties and methods. 我们在类中定义组件的应用逻辑(它被用来为视图提供支持)。 组件通过一些由属性和方法组成的API与视图交互。 - A `HeroListComponent`, for example, might have a `heroes` property that returns an array of heroes + A `HeroListComponent`, for example, might have a `heroes` property that returns !{_an} !{_array} of heroes that it acquired from a service. It might have a `selectHero()` method that sets a `selectedHero` property when the user clicks on a hero from that list. - It might be a class like this: + The component might be a class like this: 比如,在`HeroListComponent`组件中,可能有一个`heroes`属性,它返回一个英雄的数组,而这些数据是从服务中取得的。 它可能还有一个`selectHero()`方法,当用户从列表中点击一个英雄时,用它来设置`selectedHero`属性。 - 它可能是像这样的一个类: + 这个组件可能是像这样的一个类: -+makeExample('architecture/ts/app/hero-list.component.ts', 'class', 'app/hero-list.component.ts') ++makeExcerpt('app/hero-list.component.ts', 'class') :marked Angular creates, updates, and destroys components as the user moves through the application. - The developer can take action at each moment in this lifecycle through optional [Lifecycle Hooks](lifecycle-hooks.html). + The developer can take action at each moment in this lifecycle through optional [lifecycle hooks](lifecycle-hooks.html), like `ngOnInit()` declared above. 当用户在这个应用中“移动”时,Angular会创建、更新和销毁组件。 - 开发人员可以通过[生命周期钩子](lifecycle-hooks.html)在组件生命周期的各个时间点上插入自己的操作。 - + 开发人员可以通过[生命周期钩子](lifecycle-hooks.html)在组件生命周期的各个时间点上插入自己的操作,比如上面声明的`ngOnInit()`。 + .l-sub-section :marked - We're not showing those hooks in this example - but we are making a mental note to find out about them later. - - 我们不会在这个例子中展示这些钩子,先在脑子中留个记号,将来再翻出来讲。 - - We may wonder who is calling that constructor? Who provides the service parameter? + We may wonder who is calling the component's constructor? Who provides the service parameter? For the moment, have faith that Angular will call the constructor and deliver an appropriate `HeroService` when we need it. - 你可能会好奇,谁来调用那个构造函数?谁为服务提供参数? + 你可能会好奇,谁来调用那个组件的构造函数?谁为服务提供参数? 目前,你只要信任Angular会在合适的时机调用构造函数,并在需要的时候给出一个合适的`HeroService`实例。 .l-main-section - :marked - ## The Template + ## Templates + ## 模板 + figure img(src="/resources/images/devguide/architecture/template.png" alt="模板" align="left" style="width:200px; margin-left:-40px;margin-right:10px" ) :marked - We define a Component's view with its companion **template**. A template is a form of HTML - that tells Angular how to render the Component. + We define a component's view with its companion **template**. A template is a form of HTML + that tells Angular how to render the component. 我们通过组件的自带的**模板**来定义视图。模板以HTML形式存在,用来告诉Angular如何渲染组件(视图)。 A template looks like regular HTML much of the time ... and then it gets a bit strange. Here is a - template for our `HeroList` component. + template for our `HeroListComponent`: - 多数情况下,模板看起来很像标准HTML……当然也有一小点儿奇怪的地方。下面是`HeroList`组件的一个模板。 - -+makeExample('architecture/ts/app/hero-list.component.html',null,'app/hero-list.component.html') + 多数情况下,模板看起来很像标准HTML……当然也有一小点儿奇怪的地方。下面是`HeroList`组件的一个模板。 + ++makeExample('app/hero-list.component.html') + :marked - We recognize `

    ` and `
    `. - But there's other markup that no one told us about in school. - What are `*ngFor`, `{{hero.name}}`, `(click)`, `[hero]`, and ``? + This template features typical HTML elements like `

    ` and `

    `. + But what are `*ngFor`, `{{hero.name}}`, `(click)`, `[hero]`, and ``? - 我们认得`

    `和`
    `。 - 但另外那些标签/属性是我们在学校里从没听过的。 - `*ngFor`、`{{hero.name}}`、`(click)`、`[hero]`和``都是什么东西? - - These are examples of Angular's [template syntax](template-syntax.html). + 这个模版拥有典型的HTML元素,比如`

    `和`

    `。 + 但是`*ngFor`、`{{hero.name}}`、`(click)`、`[hero]`和``都是什么东西? + + These are examples of Angular's [template syntax](template-syntax.html). We will grow accustomed to that syntax and may even learn to love it. We'll begin to explain it in a moment. @@ -356,32 +389,32 @@ figure figure img(src="/resources/images/devguide/architecture/component-tree.png" alt="组件树" align="left" style="width:300px; margin-left:-40px;margin-right:10px" ) :marked - Notice how `` rests comfortably among the HTML elements we already know. - We can mix ... and will mix ... our custom components with native HTML in the same layouts. + Notice how `` rests comfortably among native HTML elements. + We can and _will_ mix our custom components with native HTML in the same layouts. 注意:``竟如此和谐的出现在那些已知的HTML元素中。 - 在同一个格局中,我们能混用……也将继续混用……自定义组件与原生HTML。 + 在同一个格局中,我们可以也将继续混用自定义组件与原生HTML。 - And in this manner we can and will compose complex component trees to build out our richly featured application. + In this manner we'll compose complex component trees to build out our richly featured application. - 在这种方式下,我们能够,并且将会组合出复杂的组件树,用来构建那些丰富多彩的应用。 + 在这种方式下,我们将会组合出复杂的组件树,用来构建那些丰富多彩的应用。
    .l-main-section - :marked - ## Angular Metadata + ## Metadata - ## Angular元数据 + ## 元数据 figure img(src="/resources/images/devguide/architecture/metadata.png" alt="元数据" align="left" style="width:150px; margin-left:-40px;margin-right:10px" ) + :marked

    Metadata tells Angular how to process a class.

    元数据告诉Angular如何处理一个类。


    :marked - [Looking back](#component-code) at the `HeroListComponent`, we see that it's just a class. + [Looking back at the code](#component-code) for `HeroListComponent`, we see that it's just a class. There is no evidence of a framework, no "Angular" in it at all. [回头看看](#component-code)`HeroListComponent`就会明白:它只是一个类。 @@ -395,82 +428,78 @@ figure 只要把**元数据**附加到这个类,就相当于告诉Angular:`HeroListComponent`是个组件。 - The easy way to attach metadata in TypeScript is with a **decorator**. + In !{_Lang}, we attach metadata by using !{_a} **!{_decorator}**. Here's some metadata for `HeroListComponent`: - 在TypeScript中,**装饰器(decorator)**是附加元数据的简易途径。 + 在!{_Lang}中,我们用**装饰器(!{_a} **!{_decorator}**)**来附加元数据。 下面就是`HeroListComponent`的一些元数据。 - -+makeExample('architecture/ts/app/hero-list.component.ts', 'metadata', 'app/hero-list.component.ts (元数据)') + ++makeExcerpt('app/hero-list.component.ts', 'metadata') + :marked - Here we see the `@Component` decorator which (no surprise) identifies the class - immediately below it as a Component class. + Here we see the `@Component` !{_decorator} which (no surprise) identifies the class + immediately below it as a component class. - 这里看到`@Component`装饰器(毫无悬念的)把紧随其后的类标记成了组件类。 + 这里看到`@Component`!{_decorator}(毫无悬念的)把紧随其后的类标记成了组件类。 - A decorator is a function. Decorators often have a configuration parameter. - The `@Component` decorator takes a required configuration object with the - information Angular needs to create and present the component and its view. - - 装饰器就是函数,它们通常还带有配置参数。 - `@Component`装饰器能接受一个配置对象,Angular会基于这些信息创建和展示组件及其视图。 - - Here we see a few of the possible `@Component` configuration options: - - 来看下`@Component`中的一些配置项: - - * `selector` - a css selector that tells Angular to create and insert an instance of this component +block ts-decorator + :marked + A decorator is a function. Decorators often have a configuration parameter. + The `@Component` decorator takes a required configuration object with the + information Angular needs to create and present the component and its view. + + 装饰器就是函数,它们通常还带有配置参数。 + `@Component`装饰器能接受一个配置对象,Angular会基于这些信息创建和展示组件及其视图。 + + Here are a few of the possible `@Component` configuration options: + + 来看下`@Component`中的一些配置项: + +:marked + - `selector` - a css selector that tells Angular to create and insert an instance of this component where it finds a `` tag in *parent* HTML. - If the template of the application shell (a Component) contained + For example, if an app's HTML contains ``, then + Angular inserts an instance of the `HeroListComponent` view between those tags. - * `selector` - 一个css选择器,它告诉Angular在*父级*HTML中寻找一个``标签,然后创建该组件,并插入此标签中。 - 比如,如果应用“壳”(组件)的模板包含如下代码: + - `selector` - 一个css选择器,它告诉Angular在*父级*HTML中寻找一个``标签,然后创建该组件,并插入此标签中。 + 比如,如果应用的HTML包含``,Angular就会插入`HeroListComponent`的一个实例到这个标签中。 -
    -code-example(language="html"). - <hero-list></hero-list> -
    -:marked - >Angular inserts an instance of the `HeroListComponent` view between those tags. - - >Angular就会在这些标签中插入`HeroListComponent`视图的一个实例。 - - * `templateUrl` - the address of this component's template which we showed [above](#template). - - * `templateUrl` - 组件模板的地址,我们在[前面](#template)见过。 - - * `directives` - an array of the Components or Directives that *this* template requires. + - `templateUrl`: address of this component's template, which we showed [above](#templates). + + - `templateUrl` - 组件模板的地址,我们在[前面](#templates)见过。 + + - `directives`: !{_array} of the components or directives that *this* template requires. We saw in the last line of our template that we expect Angular to insert a `HeroDetailComponent` - in the space indicated by `` tags. - Angular will do so only if we mention the `HeroDetailComponent` in this `directives` array. - - * `directives` - 一个数组,包含*此*模板需要依赖的组件或指令。 + in the space indicated by `` tags. + Angular will do so only if we mention the `HeroDetailComponent` in this `directives` !{_array}. + + - `directives` - 一个数组,包含*此*模板需要依赖的组件或指令。 看看模板的最后一行,这表示我们期待Angular把`HeroDetailComponent`的实例放在``标签中。 但是,只有在`directives`数组中提及到`HeroDetailComponent`的时候,Angular才会这么做。 - * `providers` - an array of **dependency injection providers** for services that the component requires. - This is one way to tell Angular that our component's constructor requires a `HeroService` - so it can get the list of heroes to display. We'll get to dependency injection in a moment. - - * `providers` - 一个数组,包含组件所依赖的服务所需要的*依赖注入提供商*。 + - `providers`: !{_array} of **dependency injection providers** for services that the component requires. + This is one way to tell Angular that our component's constructor requires a `HeroService` + so it can get the list of heroes to display. We'll get to dependency injection later. + + - `providers` - 一个数组,包含组件所依赖的服务所需要的*依赖注入提供商*。 这是在告诉Angular:该组件的构造函数需要一个`HeroService`服务,这样组件就可以从服务中获得用来显示英雄列表的那些数据。 我们一会儿就讲到了依赖注入。 + figure img(src="/resources/images/devguide/architecture/template-metadata-component.png" alt="元数据" align="left" style="height:200px; margin-left:-40px;margin-right:10px" ) :marked - The `@Component` function takes the configuration object and turns it into metadata that it attaches - to the component class definition. Angular discovers this metadata at runtime and thus knows how to do "the right thing". + Angular reads the metadata specified by the `@Component` + annotation. That's how Angular learns to do "the right thing". - `@Component`函数接收一个配置对象,并把它转换成元数据,附加到组件类的定义上。 - Angular在运行期间会找出这份元数据,并由此知道该如何去“做正确的事”。 + Angular读取`@Component`里面的元数据设置,并由此知道该如何去“做正确的事”。 The template, metadata, and component together describe the view. 模板、元数据和组件共同描绘出这个视图。 - We apply other metadata decorators in a similar fashion to guide Angular behavior. - The `@Injectable`, `@Input`, `@Output`, `@RouterConfig` are a few of the more popular decorators + We apply other metadata !{_decorator}s in a similar fashion to guide Angular behavior. + `@Injectable`, `@Input`, and `@Output` are a few of the more popular !{_decorator}s we'll master as our Angular knowledge grows. 我们也会沿用类似的风格,用其它元数据装饰器来指导Angular的行为。 @@ -479,20 +508,20 @@ figure
    :marked - The architectural take-away is that we must add metadata to our code + The architectural takeaway is that we must add metadata to our code so that Angular knows what to do. 这种架构所决定的是:必须往代码中添加元数据,以便Angular知道该做什么。 .l-main-section - :marked - ## Data Binding + ## Data binding + ## 数据绑定 Without a framework, we would be responsible for pushing data values into the HTML controls and turning user responses - into actions and value updates. Writing such push/pull logic by hand is tedious, error-prone and a nightmare to - read as the experienced jQuery programmer can attest. + into actions and value updates. Writing such push/pull logic by hand is tedious, error-prone, and a nightmare to + read as any experienced jQuery programmer can attest. 如果没有框架,我们就得自己把数据值推送到HTML控件中,并把用户的反馈转换成动作和值更新。 如果手工写代码来实现这些推/拉逻辑,肯定会枯燥乏味、容易出错,读起来简直是噩梦 —— 写过jQuery的程序员大概都对此深有体会。 @@ -500,36 +529,38 @@ figure figure img(src="/resources/images/devguide/architecture/databinding.png" alt="数据绑定" style="width:220px; float:left; margin-left:-40px;margin-right:20px" ) :marked - Angular supports **data binding**, + Angular supports **data binding**, a mechanism for coordinating parts of a template with parts of a component. We add binding markup to the template HTML to tell Angular how to connect both sides. Angular支持**数据绑定**,一种让模板的各部分与组件的各部分相互合作的机制。 我们往模板HTML中添加绑定标记,来告诉Angular如何连接两者。 - There are four forms of data binding syntax. Each form has a direction - to the DOM, from the DOM, or in both directions - + There are four forms of data binding syntax. Each form has a direction — to the DOM, from the DOM, or in both directions — as indicated by the arrows in the diagram. 数据绑定的语法有四种形式。每种形式都有一个方向 —— 从DOM来、到DOM去、双向,就像图中的箭头所示意的。
    :marked - We saw three forms of data binding in our [example](#template) template: + We saw three forms of data binding in our [example](#templates) template: 在[范例](#template)模板中,我们看到了数据绑定的三种形式: -+makeExample('architecture/ts/app/hero-list.component.1.html', 'binding', 'app/hero-list.component.html (节选)')(format=".") + ++makeExcerpt('app/hero-list.component.1.html', 'binding') + :marked * The `{{hero.name}}` [*interpolation*](displaying-data.html#interpolation) - displays the component's `hero.name` property value within the `
    ` tags. + displays the component's `hero.name` property value within the `
  • ` tags. - * `{{hero.name}}`[*插值表达式*](displaying-data.html#interpolation):在`
    `标签中显示了组件的`hero.name`属性的值。 + * `{{hero.name}}`[*插值表达式*](displaying-data.html#interpolation):在`
  • `标签中显示了组件的`hero.name`属性的值。 - * The `[hero]` [*property binding*](template-syntax.html#property-binding) passes the `selectedHero` from + * The `[hero]` [*property binding*](template-syntax.html#property-binding) passes the value of `selectedHero` from the parent `HeroListComponent` to the `hero` property of the child `HeroDetailComponent`. - * `[hero]`[*属性绑定*](template-syntax.html#property-binding):把父组件`HeroListComponent`的`selectedHero`传到子组件`HeroDetailComponent`的`hero`属性中。 + * `[hero]`[*属性绑定*](template-syntax.html#property-binding):把父组件`HeroListComponent`的`selectedHero`的值传到子组件`HeroDetailComponent`的`hero`属性中。 - * The `(click)` [*event binding*](user-input.html#click) calls the Component's `selectHero` method when the user clicks + * The `(click)` [*event binding*](user-input.html#click) calls the component's `selectHero` method when the user clicks on a hero's name * `(click)`[*事件绑定*](user-input.html#click):当用户点击英雄的名字时,调用组件的`selectHero`方法。 @@ -542,7 +573,8 @@ figure * **双向数据绑定**:这是很重要的第四种绑定形式,它在`ngModel`指令这个单一标记中同时实现了属性绑定和事件绑定的功能。 在`HeroListComponent`模板中没有双向绑定,下面是一个来自`HeroDetailComponent`模板的范例(以前没展示过): -+makeExample('architecture/ts/app/hero-detail.component.html', 'ngModel')(format=".") ++makeExcerpt('app/hero-detail.component.html', 'ngModel') + :marked In two-way binding, a data property value flows to the input box from the component as with property binding. The user's changes also flow back to the component, resetting the property to the latest value, @@ -550,17 +582,17 @@ figure 在双向绑定中,数据属性的值会从具有属性绑定的组件传到输入框。通过事件绑定,用户的修改被传回到组件,把属性值设置为最新的值。 - Angular processes *all* data bindings once per JavaScript event cycle, - depth-first from the root of the application component tree. + Angular processes *all* data bindings once per JavaScript event cycle, + from the root of the application component tree down to the leaves. Angular在每个JavaScript事件周期中一次性处理*所有的*数据绑定,它会从组件树的根部开始,用深度优先的方式处理。 figure img(src="/resources/images/devguide/architecture/component-databinding.png" alt="数据绑定" style="float:left; width:300px; margin-left:-40px;margin-right:10px" ) :marked - We don't know all the details yet + We don't know all the details yet, but it's clear from these examples that data binding plays an important role in communication - between a template and its component ... + between a template and its component. 虽然还不清楚所有细节,但是我们从这些范例中至少弄明白一点:数据绑定在模板与对应组件的交互中扮演了一个很重要的角色。 @@ -568,116 +600,118 @@ figure figure img(src="/resources/images/devguide/architecture/parent-child-binding.png" alt="父/子绑定" style="float:left; width:300px; margin-left:-40px;margin-right:10px" ) :marked - ... ***and*** between parent and child components + Data binding is also important for communication between parent and child components. - ...在父组件与子组件的通讯中***也同样如此***。 + 数据绑定在父组件与子组件的通讯中***也同样重要***。
    .l-main-section - :marked - ## The Directive + ## Directives ## 指令 figure img(src="/resources/images/devguide/architecture/directive.png" alt="父与子" style="float:left; width:150px; margin-left:-40px;margin-right:10px" ) :marked - Our Angular templates are *dynamic*. When Angular renders them, it transforms the DOM - according to the instructions given by a **directive**. + Angular templates are *dynamic*. When Angular renders them, it transforms the DOM + according to the instructions given by a **directives**. Angular模板是*动态的*。当Angular渲染它们时,它会根据**指令**提供的操作指南对DOM进行修改。 - A directive is a class with directive metadata. In TypeScript we'd apply the `@Directive` decorator + A directive is a class with directive metadata. In !{_Lang} we apply the `@Directive` !{_decorator} to attach metadata to the class. - 指令是一个带有“指令元数据”的类。在TypeScript中,要通过`@Directive`装饰器把元数据附加到类上。 + 指令是一个带有“指令元数据”的类。在!{_Lang}中,要通过`@Directive`装饰器把元数据附加到类上。
    :marked - We already met one form of directive: the component. A component is a *directive-with-a-template* - and the `@Component` decorator is actually a `@Directive` decorator extended with template-oriented features. + We already met one form of directive: the component. A component is a *directive-with-a-template*; + a `@Component` !{_decorator} is actually a `@Directive` !{_decorator} extended with template-oriented features. 我们已经遇到过指令的形式之一:组件。组件是一个*带模板的指令*,而且`@Component`装饰器实际上就是一个`@Directive`装饰器,只是扩展了一些面向模板的属性。 .l-sub-section :marked - While the **component is technically a directive**, - it is so distinctive and central to Angular applications that we chose - to separate the component from the directive in our architectural overview. + While **a component is technically a directive**, + components are so distinctive and central to Angular applications that we chose + to separate components from directives in this architectural overview. 虽然**组件从技术角度上就是一个指令**,但是组件非常独特,并在Angular中位于中心地位,以至于在架构介绍中,我们把组件从指令中单独开来。 :marked - There are two *other* kinds of directives as well that we call "structural" and "attribute" directives. + Two *other* kinds of directives exist: _structural_ and _attribute_ directives. 有两种*其它*类型的指令,我们称之为“结构型”指令和“属性(attribute)型”指令。 - They tend to appear within an element tag like attributes, + They tend to appear within an element tag as attributes do, sometimes by name but more often as the target of an assignment or a binding. - 它们往往像属性(attribute)一样出现在元素标签中,偶尔会以名字的形式出现(参见[一次性绑定](./template-syntax.html#one-time-initialization)),但多数时候还是作为赋值目标或绑定目标出现。 + 它们往往像属性(attribute)一样出现在元素标签中,偶尔会以名字的形式出现但多数时候还是作为赋值目标或绑定目标出现。 **Structural** directives alter layout by adding, removing, and replacing elements in DOM. **结构型指令**通过在DOM中添加、移除和替换元素来修改布局。 - We see two built-in structural directives at play in our [example](#template) template: + Our [example](#templates) template uses two built-in structural directives: - 我们在[范例](#template)模板中会看到两个内建的结构型指令。 + 我们在[范例](#templates)模板中会看到两个内建的结构型指令。 -+makeExample('architecture/ts/app/hero-list.component.1.html', 'structural')(format=".") ++makeExcerpt('app/hero-list.component.1.html', 'structural') :marked - * [`*ngFor`](displaying-data.html#ngFor) tells Angular to stamp out one `
    ` per hero in the `heroes` list. - + * [`*ngFor`](displaying-data.html#ngFor) tells Angular to stamp out one `
  • ` per hero in the `heroes` list. + * [`*ngFor`](displaying-data.html#ngFor)告诉Angular为`heroes`列表中的每个英雄生成一个`
    `标签。 - + * [`*ngIf`](displaying-data.html#ngIf) includes the `HeroDetail` component only if a selected hero exists. - + * [`*ngIf`](displaying-data.html#ngIf)表示只有在选择的英雄存在时,才会包含`HeroDetail`组件。 - **Attribute** directives alter the appearance or behavior of an existing element. - In templates they look like regular HTML attributes, hence the name. +block dart-bool + //- N/A - **属性型指令** 修改一个现有元素的外观或行为。在模板中,它们看起来就像是标准的HTML属性,故名。 - - The `ngModel` directive, which implements two-way data binding, is an example of an attribute directive. - - `ngModel`指令就是属性型指令的一个例子,它实现了双向数据绑定。 - -+makeExample('architecture/ts/app/hero-detail.component.html', 'ngModel')(format=".") :marked - It modifies the behavior of an existing element (typically an ``) - by setting its display value property and responding to change events. - - 它修改了现有元素(典型的``)的行为:它设置了显示属性值,并对change事件做出相应回应。 - - Angular ships with a few other directives that either alter the layout structure - (e.g. [ngSwitch](template-syntax.html#ngSwitch)) - or modify aspects of DOM elements and components - (e.g. [ngStyle](template-syntax.html#ngStyle) and [ngClass](template-syntax.html#ngClass)). + **Attribute** directives alter the appearance or behavior of an existing element. + In templates they look like regular HTML attributes, hence the name. + + **属性型指令** 修改一个现有元素的外观或行为。在模板中,它们看起来就像是标准的HTML属性,故名。 + + The `ngModel` directive, which implements two-way data binding, is + an example of an attribute directive. `ngModel` modifies the behavior of + an existing element (typically an ``) + by setting its display value property and responding to change events. + + `ngModel`指令就是属性型指令的一个例子,它实现了双向数据绑定。它修改了现有元素(一般是``)的行为:它设置了显示属性值,并对change事件做出相应回应。 + ++makeExcerpt('app/hero-detail.component.html', 'ngModel') +:marked + Angular ships with a small number of other directives that either alter the layout structure + (for example, [ngSwitch](template - syntax.html#ngSwitch)) + or modify aspects of DOM elements and components + (for example, [ngStyle](template-syntax.html#ngStyle) and [ngClass](template-syntax.html#ngClass)). Angular内置了一些其它指令,它们或者修改结构布局(如[ngSwitch](template-syntax.html#ngSwitch)),或者修改DOM元素和组件的各个方面 (如[ngStyle](template-syntax.html#ngStyle)和[ngClass](template-syntax.html#ngClass))。 - And of course we can write our own directives. + Of course, we can also write our own directives. Components such as + `HeroListComponent` are one kind of custom directive. + - 当然,我们也能编写自己的指令。 + 当然,我们也能编写自己的指令。像`HeroListComponent`这样的组件就是一种自定义指令。 .l-main-section - :marked - ## The Service + ## Services ## 服务 - + figure img(src="/resources/images/devguide/architecture/service.png" alt="服务" style="float:left; margin-left:-40px;margin-right:10px" ) :marked - "Service" is a broad category encompassing any value, function or feature that our application needs. + _Service_ is a broad category encompassing any value, function or feature that our application needs. - “服务”分为很多种,包括:值、函数,以及应用所需的特性。 + *服务*分为很多种,包括:值、函数,以及应用所需的特性。 Almost anything can be a service. A service is typically a class with a narrow, well-defined purpose. It should do something specific and do it well. @@ -710,36 +744,38 @@ figure * 应用程序配置 - There is nothing specifically *Angular* about services. Angular itself has no definition of a *service*. - There is no *ServiceBase* class. + There is nothing specifically _Angular_ about services. Angular itself has no definition of a service. + There is no service base class, and no place to register a service. 服务没有什么特别属于Angular的特性。Angular本身对于服务也没有什么定义。 - 它甚至都没有*ServiceBase*类。 + 它甚至都没有服务基础类,也没有地方注册一个服务。 - Yet services are fundamental to any Angular application. + Yet services are fundamental to any Angular application. Our components are big consumers of services. - 即便如此,服务仍然是任何Angular应用的基础。 + 即便如此,服务仍然是任何Angular应用的基础。组件就是最大的*服务*消费者。 Here's an example of a service class that logs to the browser console 这里是一个“服务”类的范例,用于把日志记录到浏览器的控制台: -+makeExample('architecture/ts/app/logger.service.ts', 'class', 'app/logger.service.ts (只有类)')(format=".") ++makeExcerpt('app/logger.service.ts', 'class') + :marked - Here's a `HeroService` that fetches heroes and returns them in a resolved [promise](http://exploringjs.com/es6/ch_promises.html). + Here's a `HeroService` that fetches heroes and returns them in a resolved !{_PromiseLinked}. The `HeroService` depends on the `Logger` service and another `BackendService` that handles the server communication grunt work. 下面是`HeroService`类,用于获取英雄数据,并通过一个已解析的[承诺Promise](http://exploringjs.com/es6/ch_promises.html)返回它们。 `HeroService`还依赖于`Logger`服务和另一个用来处理服务器通讯工作的`BackendService`服务。 - -+makeExample('architecture/ts/app/hero.service.ts', 'class', 'app/hero.service.ts (只有类)')(format=".") + ++makeExcerpt('app/hero.service.ts', 'class') + :marked Services are everywhere. - 服务无处不在。 + 服务无处不在。 - Our components are big consumers of services. They depend upon services to handle most chores. - They don't fetch data from the server, they don't validate user input, they don't log directly to the console. + We prefer our component classes lean. Our components don't fetch data from the server, + they don't validate user input, and they don't log directly to the console. They delegate such tasks to services. 组件是服务的主要消费者。它们依赖服务来处理大多数“苦差事”。 @@ -747,13 +783,14 @@ figure 它们把这些任务委托给服务。 A component's job is to enable the user experience and nothing more. It mediates between the view (rendered by the template) - and the application logic (which often includes some notion of a "model"). A good component presents - properties and methods for data binding. It delegates everything non-trivial to services. + and the application logic (which often includes some notion of a _model_). + A good component presents properties and methods for data binding. + It delegates everything nontrivial to services. 组件的任务就是提供用户体验,仅此而已。它介于视图(由模板渲染)和应用逻辑(通常包括“模型model”的观念)之间。 设计良好的组件为数据绑定提供属性和方法,把那些其他对它们不重要的事情都委托给服务。 - Angular doesn't *enforce* these principles. + Angular doesn't *enforce* these principles. It won't complain if we write a "kitchen sink" component with 3000 lines. Angular不会*强制要求*我们遵循这些原则。 @@ -765,16 +802,15 @@ figure Angular帮助我们*追随*这些原则 —— 它让我们能更轻易的把应用逻辑拆分到服务,并通过*依赖注入*来在组件中使用这些服务。 .l-main-section - :marked ## Dependency Injection ## 依赖注入 - + figure img(src="/resources/images/devguide/architecture/dependency-injection.png" alt="服务" style="float:left; width:200px; margin-left:-40px;margin-right:10px" ) :marked - "Dependency Injection" is a way to supply a new instance of a class + _Dependency injection_ is a way to supply a new instance of a class with the fully-formed dependencies it requires. Most dependencies are services. Angular uses dependency injection to provide new components with the services they need. @@ -783,22 +819,24 @@ figure
    :marked - In TypeScript, Angular can tell which services a component needs by looking at the types of its constructor parameters. - For example, the constructor of our `HeroListComponent` needs the `HeroService`: + Angular can tell which services a component needs by looking at the types of its constructor parameters. + For example, the constructor of our `HeroListComponent` needs a `HeroService`: - 在TypeScript里,Angular能通过查看构造函数的参数类型,来得知组件需要哪些服务。 + Angular能通过查看构造函数的参数类型,来得知组件需要哪些服务。 例如,`HeroListComponent`组件的构造函数需要`HeroService`: -+makeExample('architecture/ts/app/hero-list.component.ts', 'ctor', 'app/hero-list.component (构造函数)')(format=".") + ++makeExcerpt('app/hero-list.component.ts (constructor)', 'ctor') + :marked - When Angular creates a component, it first asks an **Injector** for + When Angular creates a component, it first asks an **injector** for the services that the component requires. 当Angular创建组件时,会首先为组件所需的服务找一个**注入器Injector**。 - An `Injector` maintains a container of service instances that it has previously created. + An `injector` maintains a container of service instances that it has previously created. If a requested service instance is not in the container, the injector makes one and adds it to the container - before returning the service to Angular. - When all requested services have been resolved and returned, + before returning the service to Angular. + When all requested services have been resolved and returned, Angular can call the component's constructor with those services as arguments. This is what we mean by *dependency injection*. @@ -814,7 +852,7 @@ figure figure img(src="/resources/images/devguide/architecture/injector-injects.png" alt="服务" ) :marked - If the `Injector` doesn't have a `HeroService`, how does it know how to make one? + If the injector doesn't have a `HeroService`, how does it know how to make one? 如果注入器还没有`HeroService`,它怎么知道该如何创建一个呢? @@ -831,34 +869,36 @@ figure 可以在应用程序的组件树中的任何级别上注册提供商。 当需要一个服务的同一个实例在任何地方都是可用时,我们通常在应用引导程序中注册它。 -+makeExample('architecture/ts/app/main.ts', 'bootstrap','app/main.ts (节选)')(format=".") ++makeExcerpt('app/main.ts', 'bootstrap') + :marked - Alternatively, we might register at a component level ... + Alternatively, we might register at a component level, in the providers property of the `@Component` metadata: - 或者,也可以把它注册在组件层…… + 或者,也可以在`@Component`元数据中的`providers`属性中把它注册在组件层: -+makeExample('architecture/ts/app/hero-list.component.ts', 'providers','app/hero-list.component.ts (节选)')(format=".") ++makeExcerpt('app/hero-list.component.ts', 'providers') + :marked - ... in which case we get a new instance of the + Registering at a component level means we get a new instance of the service with each new instance of that component. ... 在这种情况下,那个组件的每一个新实例都会有一个(在该组件注册的)服务的新实例。 - We've vastly over-simplified dependency injection for this overview. - We can learn the full story in the [Dependency Injection](dependency-injection.html) chapter. + - 在这个概览中,我们过度简化了依赖注入机制。 - 在[依赖注入](dependency-injection.html)一章中,我们能学到关于它更详尽的知识。 + - The points to remember are: + Points to remember about dependency injection: - 需要记住的要点是: + 需要记住的关于依赖注入的要点是: - * dependency injection is wired into the framework and used everywhere. + * Dependency injection is wired into the Angular framework and used everywhere. - * 依赖注入渗透在整个框架中,并且被到处使用。 + * 依赖注入渗透在整个Angular框架中,并且被到处使用。 - * the `Injector` is the main mechanism. + * the `injector` is the main mechanism. * 注入器`Injector`是本机制的核心。 @@ -878,7 +918,14 @@ figure * 把*提供商*注册到注入器。 - + * The *injector* is the main mechanism. + * An injector maintains a *container* of service instances that it created. + * An injector can create a new service instance from a *provider*. + + * A *provider* is a recipe for creating a service. + + * We register *providers* with injectors. + .l-main-section :marked ## Wrap up @@ -889,35 +936,35 @@ figure 我们学到的这些只是关于Angular应用程序的八个主要构造块的一点皮毛 - 1. [Module](#module) + 1. [Modules](#modules) - 1. [模块](#module) + 1. [模块](#modules) - 1. [Component](#component) + 1. [Components](#components) - 1. [组件](#component) + 1. [组件](#components) - 1. [Template](#template) + 1. [Templates](#templates) - 1. [模板](#template) + 1. [模板](#templates) 1. [Metadata](#metadata) 1. [元数据](#metadata) - 1. [Data Binding](#data-binding) + 1. [Data binding](#data-binding) 1. [数据绑定](#data-binding) - 1. [Directive](#directive) + 1. [Directives](#directives) - 1. [指令](#directive) + 1. [指令](#directives) - 1. [Service](#service) + 1. [Services](#services) - 1. [服务](#service) + 1. [服务](#services) - 1. [Dependency Injection](#dependency-injection) + 1. [Dependency injection](#dependency-injection) 1. [依赖注入](#dependency-injection) @@ -929,74 +976,74 @@ figure 这是Angular应用程序中所有其它东西的基础,要使用Angular 2,以这些作为开端就绰绰有余了。 但它仍然没有包含我们需要的或想知道的全部。 - -.l-main-section -:marked - ## The Other Stuff - - ## 其它东西 - Here is a brief, alphabetical list of other important Angular features and services. Most of them are covered in this Developers Guide (or soon will be): 这里是一个简短的、按字母排序的列表,列出了其它重要的Angular特性和服务。 它们大多数已经(或即将)包括在这个《开发指南》中: - >**Animations** - A forthcoming animation library makes it easy for developers to animate component behavior - without deep knowledge of animation techniques or css. + > [**Animations**](animations.html): The animation library makes it easy for developers to animate component behavior + without deep knowledge of animation techniques or CSS. - >**动画Animations** - 即将到来的动画库。它能让开发人员更轻易的给组件添加动画行为,而不需要对动画技术或css有深入的了解。 + > [**动画Animations**](animations.html) : 即将到来的动画库。它能让开发人员更轻易的给组件添加动画行为,而不需要对动画技术或css有深入的了解。 - >**Bootstrap** - A method to configure and launch the root application component. + > **Bootstrap**: A method to configure and launch the root application component. - >**引导Bootstrap** - 配置和引导应用的根组件的方法。 + > **引导Bootstrap**: 配置和引导应用的根组件的方法。 - >**Change Detection** - Learn how Angular decides that a component property value has changed and + > **Change detection**: Learn how Angular decides that a component property value has changed and when to update the screen. Learn how it uses **zones** to intercept asynchronous activity and run its change detection strategies. - >**变更检测Change Detection** - 学会Angular是如何决定组件的属性值变化和什么时候该更新到屏幕的。 + > **变更检测Change Detection**:学会Angular是如何决定组件的属性值变化和什么时候该更新到屏幕的。 学会它如何使用**zones**来拦截异步行为和它如何执行变更检测策略。 - >**[Component Router](router.html)** - With the Component Router service, users can navigate a multi-screen application + > **Component router**: With the component Router service, users can navigate a multi-screen application in a familiar web browsing style using URLs. - >**[组件路由Component Router](router.html)** - 通过组件路由服务,可以让用户使用浏览器中熟悉的URL形式,在多屏应用之间导航。 + > **组件路由Component Router** - 通过组件路由服务,可以让用户使用浏览器中熟悉的URL形式,在多屏应用之间导航。 - >**Events** - The DOM raises events. So can components and services. Angular offers mechanisms for + > **Events**: The DOM raises events. So can components and services. Angular offers mechanisms for publishing and subscribing to events including an implementation of the [RxJS Observable](https://github.com/zenparsing/es-observable) proposal. - >**事件Events** - DOM能触发事件,组件和服务也能。Angular提供的事件发布与订阅机制还包括[RxJS可观察Observable](https://github.com/zenparsing/es-observable)方案的一个实施。 + > **事件Events** - DOM能触发事件,组件和服务也能。Angular提供的事件发布与订阅机制还包括[RxJS可观察Observable](https://github.com/zenparsing/es-observable)方案的一个实施。 - >**[Forms](forms.html)** - Support complex data entry scenarios with HTML-based validation and dirty checking. + > [**Forms**](forms.html): Support complex data entry scenarios with HTML-based validation and dirty checking. - >**[表单Forms](forms.html)** - 通过基于HTML的验证和脏检查机制支持复杂的数据输入场景。 + > [**表单Forms**](forms.html):通过基于HTML的验证和脏检查机制支持复杂的数据输入场景。 - >**[HTTP](server-communication.html)** - Communicate with a server to get data, save data, and invoke server-side actions with this Angular HTTP client. + > [**HTTP**](server-communication.html): Communicate with a server to get data, save data, and invoke server-side actions with this Angular HTTP client. - >**[HTTP](server-communication.html)** - 通过这个Angular HTTP客户端,可以与服务器通讯,以获得数据、保存数据和触发服务端动作。 + > [**HTTP**](server-communication.html):通过这个Angular HTTP客户端,可以与服务器通讯,以获得数据、保存数据和触发服务端动作。 - >**[Lifecycle Hooks](lifecycle-hooks.html)** - We can tap into key moments in the lifetime of a component, from its creation to its destruction, - by implementing the "Lifecycle Hook" interfaces. + > [**Lifecycle hooks**](lifecycle-hooks.html): We can tap into key moments in the lifetime of a component, from its creation to its destruction, + by implementing the lifecycle hook interfaces. - >**[生命周期钩子Lifecycle Hooks](lifecycle-hooks.html)** - 通过实现“生命周期钩子”接口,可以切入组件生命中的几个关键点:从创建到销毁。 + > [**生命周期钩子Lifecycle Hooks**](lifecycle-hooks.html):通过实现生命周期钩子接口,可以切入组件生命中的几个关键点:从创建到销毁。 - >**[Pipes](pipes.html)** - Services that transform values for display. - We can put pipes in our templates to improve the user experience. For example, + > [**Pipes**](pipes.html): Services that transform values for display. + We can put pipes in our templates to improve the user experience. Consider this `currency` pipe expression, - >**[管道Pipes](pipes.html)** - 这种服务会转换值以供显示。可以把管道放在模板中,以增强用户体验。比如这个`currency`管道表达式, + > [**管道Pipes**](pipes.html):这种服务会转换值以供显示。可以把管道放在模板中,以增强用户体验。比如这个`currency`管道表达式,
    -code-example(language="javascript" linenumbers="."). +code-example(). price | currency:'USD':true
    :marked - >displays a price of "42.33" as `$42.33`. + > It displays a price of "42.33" as `$42.33`. - >把"42.33"显示为`$42.33`。 + > 它把"42.33"显示为`$42.33`。 - >**[Testing](testing.html)** - Angular provides a testing library for "unit testing" our application parts as they - interact with the Angular framework. - - >**[Testing](testing.html)** - Angular提供了一个测试库,在程序各个部分与Angular框架交互同时,用来“单元测试”它们。 + > [**Router**](router.html): Navigate from page to page within the client + application and never leave the browser. + + > [**路由**](router.html):在应用程序客户端导航,并且不离开浏览器。 + + > [**Testing**](testing.html): Angular provides a + [testing library](https://pub.dartlang.org/packages/angular2_testing) + to run unit tests on our application parts as they interact with the Angular framework. + + > [**测试Testing**](testing.html):Angular提供了一个测试库(https://pub.dartlang.org/packages/angular2_testing), + 在程序各个部分与Angular框架交互同时,用来“单元测试”它们。 diff --git a/public/docs/ts/latest/guide/dependency-injection.jade b/public/docs/ts/latest/guide/dependency-injection.jade index 1db77fa9c2..bdd91fedf4 100644 --- a/public/docs/ts/latest/guide/dependency-injection.jade +++ b/public/docs/ts/latest/guide/dependency-injection.jade @@ -806,10 +806,27 @@ code-example(format="nocode"). +makeExample('dependency-injection/ts/app/providers.component.ts','providers-1') -p - | This is actually a short-hand expression for a provider registration - block canonical-provider-expr - |  using a provider object literal with two properties: +:marked + This is actually a short-hand expression for a provider registration + + 这实际上是注册提供商的一种简写表达式。 + + + using a _provider_ object literal with two properties: + + + + 使用一个拥有两个属性的_provider_对象: + + + + that creates a new instance of the + [Provider](../api/core/index/Provider-class.html) class: + + + + 创建[Provider](../api/core/index/Provider-class.html)类的一个实例: + +makeExample('dependency-injection/ts/app/providers.component.ts','providers-3') diff --git a/public/docs/ts/latest/guide/forms-deprecated.jade b/public/docs/ts/latest/guide/forms-deprecated.jade index ed3699f402..0be1f0bfff 100644 --- a/public/docs/ts/latest/guide/forms-deprecated.jade +++ b/public/docs/ts/latest/guide/forms-deprecated.jade @@ -380,7 +380,7 @@ figure.image-display We'll talk about `NgForm` [later in the chapter](#ngForm). The `ngControl` *attribute* in our template actually maps to the - [NgControlName](../api/common/NgControlName-directive.html) directive. + [NgControlName](../api/common/index/NgControlName-directive.html) directive. There is also a `NgControl` *abstract* directive which is *not the same thing*. We often ignore this technical distinction and refer to `NgControlName` more conveniently (albeit incorrectly) as the *NgControl* directive. @@ -483,7 +483,7 @@ figure.image-display .l-sub-section :marked Why "ngForm"? - A directive's [exportAs](../api/core/DirectiveMetadata-class.html#!#exportAs) property + A directive's [exportAs](../api/core/index/DirectiveMetadata-class.html#!#exportAs) property tells Angular how to link the reference variable to the directive. We set `name` to `ngForm` because the `NgControlName` directive's `exportAs` property happens to be "ngForm". @@ -589,7 +589,7 @@ figure.image-display .l-sub-section :marked ### The NgForm directive - What `NgForm` directive? We didn't add an [NgForm](../api/common/NgForm-directive.html) directive! + What `NgForm` directive? We didn't add an [NgForm](../api/common/index/NgForm-directive.html) directive! Angular did. Angular creates and attaches an `NgForm` directive to the `` tag automatically. diff --git a/public/docs/ts/latest/guide/forms.jade b/public/docs/ts/latest/guide/forms.jade index c5fe5af688..ee4d723341 100644 --- a/public/docs/ts/latest/guide/forms.jade +++ b/public/docs/ts/latest/guide/forms.jade @@ -1064,7 +1064,7 @@ figure.image-display ### NgForm指令 - What `NgForm` directive? We didn't add an [NgForm](../api/common/NgForm-directive.html) directive! + What `NgForm` directive? We didn't add an [NgForm](../api/common/index/NgForm-directive.html) directive! 什么`NgForm`指令?我们没有添加过[NgForm](../api/common/index/NgForm-directive.html)指令啊! diff --git a/public/docs/ts/latest/guide/glossary.jade b/public/docs/ts/latest/guide/glossary.jade index a66284569e..ca1429104e 100644 --- a/public/docs/ts/latest/guide/glossary.jade +++ b/public/docs/ts/latest/guide/glossary.jade @@ -1 +1 @@ -!= partial("../glossary") +include ../glossary diff --git a/public/docs/ts/latest/guide/pipes.jade b/public/docs/ts/latest/guide/pipes.jade index b84c570632..ba2adb5e36 100644 --- a/public/docs/ts/latest/guide/pipes.jade +++ b/public/docs/ts/latest/guide/pipes.jade @@ -52,7 +52,7 @@ block includes :marked Inside the interpolation expression we flow the component's `birthday` value through the - [pipe operator](./template-syntax.html#pipe) ( | ) to the [Date pipe](../api/common/DatePipe-class.html) + [pipe operator](./template-syntax.html#pipe) ( | ) to the [Date pipe](../api/common/index/DatePipe-class.html) function on the right. All pipes work this way. 在这个插值表达式中,我们让组件的`birthday`值通过[管道操作符](./template-syntax.html#pipe)( | )流动到 @@ -140,7 +140,7 @@ figure.image-display .l-sub-section :marked - Learn more about the `DatePipes` format options in the [API Docs](../api/common/DatePipe-class.html). + Learn more about the `DatePipes` format options in the [API Docs](../api/common/index/DatePipe-class.html). 要了解更多`DatePipes`的格式选项,请参阅[API文档](../api/common/index/DatePipe-class.html)。 @@ -624,7 +624,7 @@ figure.image-display header Debugging with the json pipe header 借助json管道进行调试 :marked - The [JsonPipe](../api/common/JsonPipe-class.html) + The [JsonPipe](../api/common/index/JsonPipe-class.html) provides an easy way to diagnosis a mysteriously failing data binding or inspect an object for future binding. diff --git a/public/docs/ts/latest/guide/router-deprecated.jade b/public/docs/ts/latest/guide/router-deprecated.jade index d55523a082..5e758ce887 100644 --- a/public/docs/ts/latest/guide/router-deprecated.jade +++ b/public/docs/ts/latest/guide/router-deprecated.jade @@ -1465,7 +1465,7 @@ code-example(format=".", language="bash"). .l-sub-section :marked - Learn about the [APP_BASE_HREF](../api/router/APP_BASE_HREF-let.html) + Learn about the [APP_BASE_HREF](../api/common/index/APP_BASE_HREF-let.html) in the API Guide. :marked ### *HashLocationStrategy* diff --git a/public/docs/ts/latest/guide/router.jade b/public/docs/ts/latest/guide/router.jade index dd8ad88e14..817fd8dda2 100644 --- a/public/docs/ts/latest/guide/router.jade +++ b/public/docs/ts/latest/guide/router.jade @@ -231,11 +231,11 @@ include ../_util-fns 我们将把这份配置数组传给`provideRouter()`函数,它返回一个经过配置的*Router*[服务提供商](dependency-injection.html#!#injector-providers)(以及别的东西)。 - Finally, we export this provider in the `APP_ROUTER_PROVIDERS` array + Finally, we export this provider in the `appRouterProviders` array so we can simplify registration of router dependencies later in `main.ts`. We don't have any other providers to register right now. But we will. - 最后,我们通过`APP_ROUTER_PROVIDERS`数组导出这个提供商,以便我们以后将来在`main.ts`中简单的注册路由器依赖。 + 最后,我们通过`appRouterProviders`数组导出这个提供商,以便我们以后将来在`main.ts`中简单的注册路由器依赖。 目前我们还没有注册任何别的提供商,但很快就会这么做了! :marked @@ -743,11 +743,11 @@ h4#define-routes 定义一些路由 我们的第一个配置中定义了由两个路由构成的数组,它们分别通过路径(path)导航到了`CrisisListComponent`和`HeroListComponent`组件。 - Each definition translates to a [Route](../api/router/index/Route-class.html) object which has a + Each definition translates to a [Route](../api/router/index/Route-interface.html) object which has a `path`, the URL path segment for this route, and a `component`, the component associated with this route. - 每个定义都被翻译成了一个[Route](../api/router/index/Route-class.html)对象。该对象有一个`path`字段,表示该路由中的URL路径部分,和一个`component`字段,表示与该路由相关联的组件。 + 每个定义都被翻译成了一个[Route](../api/router/index/Route-interface.html)对象。该对象有一个`path`字段,表示该路由中的URL路径部分,和一个`component`字段,表示与该路由相关联的组件。 The router draws upon its registry of such route definitions when the browser URL changes or when our code tells the router to navigate along a route path. @@ -780,15 +780,15 @@ h4#provideRouter 调用provideRouter 我们把路由配置传给`provideRouter`函数,它返回一个数组,其中包括配置好的`Router`服务提供商……和路由库所需的某些你从未见过的提供商。 :marked - We add the `provideRouter` array to an `APP_ROUTER_PROVIDERS` array and export it. + We add the `provideRouter` array to an `appRouterProviders` array and export it. 我们把`provideRouter`数组加进一个`APP_ROUTER_PROVIDERS`数组,并导出它。 - We could add *additional* service providers to `APP_ROUTER_PROVIDERS` — + We could add *additional* service providers to `appRouterProviders` — providers that are specific to our routing configuration. We don't have any yet. We will have some later in this chapter. - 我们还可以往`APP_ROUTER_PROVIDERS`中添加一些*额外的*提供商,这取决于路由的具体配置。 + 我们还可以往`appRouterProviders`中添加一些*额外的*提供商,这取决于路由的具体配置。 虽然目前还一个都没有呢,不过稍后就会看到了。 .l-sub-section @@ -809,10 +809,10 @@ h4#register-providers 在启动时注册路由 本应用的启动点位于`/app`目录下的`main.ts`文件中。 这个文件很短,并且和默认的`main.ts`没有什么不同。 - The important difference: we import the `APP_ROUTER_PROVIDERS` array + The important difference: we import the `appRouterProviders` array and pass it as the second parameter of the `bootstrap` function. - 最重要的不同点是:这里我们导入了`APP_ROUTER_PROVIDERS`数组,并且把它作为第二个参数传给了`bootstrap`函数。 + 最重要的不同点是:这里我们导入了`appRouterProviders`数组,并且把它作为第二个参数传给了`bootstrap`函数。 +makeExample('router/ts/app/main.1.ts','all', 'main.ts')(format=".") @@ -1896,7 +1896,7 @@ code-example(format=""). 当用户点击了“Crisis Center”链接或者在地址栏粘贴`localhost:3000/crisis-center/`时,我们更希望该应用能直接显示危机列表。这就是默认路由。 - The preferred solution is to add a `redirect` route that transparently translates from the initial relative URL (`''`) + The preferred solution is to add a `redirect` route that transparently translates from the initial relative URL (`''`) to the desired default path (`/crisis-center`): 首选的解决方案是添加一个`redirect`路由,它会把初始的相对URL(`''`)悄悄翻译成默认路径(`/crisis-center`)。 @@ -1914,21 +1914,21 @@ code-example(format=""). .l-sub-section :marked - Technically, `pathMatch = 'full'` results in a route hit when the *remaining*, unmatched segments of the URL match `''`. + Technically, `pathMatch = 'full'` results in a route hit when the *remaining*, unmatched segments of the URL match `''`. In our example, the redirect is at the top level of the route configuration tree so the *remaining* URL and the *entire* URL are the same thing. - The other possible `pathMatch` value is `'prefix'` which tells the router + The other possible `pathMatch` value is `'prefix'` which tells the router to match the redirect route when the *remaining* URL ***begins*** with the redirect route's _prefix_ path. - - That's not what we want to do here. If the `pathMatch` value were `'prefix'`, - _every_ URL would match `''`. - We could never navigate to `/crisis-center/1` because the redirect route would match first and + + That's not what we want to do here. If the `pathMatch` value were `'prefix'`, + _every_ URL would match `''`. + We could never navigate to `/crisis-center/1` because the redirect route would match first and send us to the `CrisisListComponent`. - + We should redirect to the `CrisisListComponent` _only_ when the _entire (remaining)_ url is `''`. - Learn more in Victor Savkin's blog + Learn more in Victor Savkin's blog [post on redirects](http://victorsavkin.com/post/146722301646/angular-router-empty-paths-componentless-routes). We'll discuss redirects in more detail in a future update to this chapter. @@ -2357,10 +2357,10 @@ h3#can-deactivate-guard CanDeactivate:处理未保存的更改 +makeExample('router/ts/app/crisis-center/crisis-center.routes.4.ts', '', 'crisis-center.routes.ts') :marked - We also need to add the `Guard` to our main `APP_ROUTER_PROVIDERS` so the `Router` can inject it during the navigation process. - - 我们还要把这个`Guard`添加到主文件的`APP_ROUTER_PROVIDERS`中去,以便`Router`可以在导航过程中注入它。 + We also need to add the `Guard` to our main `appRouterProviders` so the `Router` can inject it during the navigation process. + 我们还要把这个`Guard`添加到主文件的`appRouterProviders`中去,以便`Router`可以在导航过程中注入它。 + +makeExample('router/ts/app/app.routes.ts', '', 'app.routes.ts') :marked diff --git a/public/docs/ts/latest/guide/server-communication.jade b/public/docs/ts/latest/guide/server-communication.jade index 920315921f..2bb5b661b7 100644 --- a/public/docs/ts/latest/guide/server-communication.jade +++ b/public/docs/ts/latest/guide/server-communication.jade @@ -684,7 +684,7 @@ code-example(format="." language="javascript"). +ifDocsFor('ts') :marked - [Headers](../api/http/Headers-class.html) are one of the [RequestOptions](../api/http/RequestOptions-class.html). + [Headers](../api/http/index/Headers-class.html) are one of the [RequestOptions](../api/http/index/RequestOptions-class.html). Compose the options object and pass it in as the *third* parameter of the `post` method, as shown above. [Headers](../api/http/index/Headers-class.html)是[RequestOptions](../api/http/index/RequestOptions-class.html)中的一员。 diff --git a/public/docs/ts/latest/guide/style-guide.jade b/public/docs/ts/latest/guide/style-guide.jade index 7755b475f8..79a99cba07 100644 --- a/public/docs/ts/latest/guide/style-guide.jade +++ b/public/docs/ts/latest/guide/style-guide.jade @@ -108,10 +108,6 @@ a(id='toc') 1. [生命周期钩子](#lifecycle-hooks) - 1. [Routing](#routing) - - 1. [路由](#routing) - 1. [Appendix](#appendix) 1. [附录](#appendix) @@ -121,11 +117,10 @@ a(id='toc') ## Single Responsibility ## 单一职责 - We apply the [Single Responsibility Principle](https:\/\/en.wikipedia.org/wiki/Single_responsibility_principle) to all Components, Services, and other symbols we create. - This helps make our app cleaner, easier to read and maintain, and more testable. + We apply the [Single Responsibility Principle](https://wikipedia.org/wiki/Single_responsibility_principle) to all Components, Services, and other symbols we create. This helps make our app cleaner, easier to read and maintain, and more testable. 我们遵循[单一职责原则](https:\/\/en.wikipedia.org/wiki/Single_responsibility_principle)来创建的所有组件、服务和其它标志等。这样能帮助我们把应用程序弄的干净整洁,易于阅读、维护和测试。 - + ### Rule of One ### 单一法则 #### Style 01-01 diff --git a/public/docs/ts/latest/testing/first-app-tests.jade b/public/docs/ts/latest/testing/first-app-tests.jade index b891f90973..12c3b3038e 100644 --- a/public/docs/ts/latest/testing/first-app-tests.jade +++ b/public/docs/ts/latest/testing/first-app-tests.jade @@ -33,7 +33,7 @@ include ../_util-fns We're also assuming that you're already comfortable with basic Angular 2 concepts and the tools we introduced in the [QuickStart](../quickstart.html) and the [Tour of Heroes](../tutorial/) tutorial - such as npm, gulp, and live-server. + such as npm, gulp, and lite-server. 这一章单元测试是在其它章节的基础上写的。我们建议你按顺序阅读它们。同时,我们假设你已经对Angular 2的原理、[“快速起步”](../quickstart.html)和 [英雄指南](../tutorial)中介绍的npmgulplive-server等工具都已经有所了解。 @@ -72,25 +72,38 @@ include ../_util-fns pre.prettyprint.lang-bash code npm install jasmine-core --save-dev --save-exact -.alert.is-important - :marked - Be sure to install jasmine-core , not jasmine! +.alert.is-important Be sure to install jasmine-core , not jasmine! + +.alert.is-important 请确保安装的是jasmine-core,而不是jasmine! + +.l-main-section +:marked + ## Configure `lite-server` for serving our test harness + + ## 配置`lite-server`来提供测试环境 + +:marked + First create a configuration file for serving up our test harness through `lite-server`. + + 首先为提供测试环境的`lite-server`创建一个配置文件。 + ++makeExample('testing/ts/liteserver-test-config.json', '', 'liteserver-test-config.json') - 请确保安装的是jasmine-core,而不是jasmine! :marked Let's make one more change to the `package.json` script commands. 让我们在`package.json`的脚本命令区再做一项修改。 - **Open the `package.json` ** and scroll to the `scripts` node and add in a new one: + **Open the `package.json` ** and scroll to the `scripts` node and add the following two entries: - **打开`package.json`**,滚动到`scripts`节点,添加下面这一行: + **打开`package.json`**,滚动到`scripts`节点,添加下面两行: code-example(format=""). - "test": "live-server --open=unit-tests.html" + "lite-server-test": "lite-server --config=liteserver-test-config.json", + "test": "tsc && concurrently \"npm run tsc:w\" \"npm run lite-server-test\" " :marked - That command will launch `live-server` and open a browser to the `unit-tests.html` page we just wrote. + The `npm test` command will launch `lite-server` and open a browser to the `unit-tests.html` page we just wrote. It will also take care of recompiling your source code and reloading your browser after any change. 该指令将启动`live-server`,并在浏览器中打开我们刚写的`unit-tests.html`页面。 @@ -236,7 +249,7 @@ code-example(format=""). ### Run and Fail ### 运行,并失败 - Look over at the browser (live-server will have reloaded it). The browser displays + Look over at the browser (lite-server will have reloaded it). The browser displays 看看浏览器(live-server应该已经刷新了它)。其中显示的是: diff --git a/public/docs/ts/latest/testing/jasmine-testing-101.jade b/public/docs/ts/latest/testing/jasmine-testing-101.jade index 36d2d4ecb7..799e14cbd2 100644 --- a/public/docs/ts/latest/testing/jasmine-testing-101.jade +++ b/public/docs/ts/latest/testing/jasmine-testing-101.jade @@ -191,7 +191,7 @@ pre.prettyprint.lang-bash 我们希望这些测试能快速进化,如果浏览器能在我们做出修改和重新编译时自动刷新就好了。 - Let’s launch with **live-server** in a second terminal window: + Let’s launch with **lite-server** in a second terminal window: 没问题,让我们在另一个终端窗口中启动**live-server**: diff --git a/public/docs/ts/latest/testing/testing-an-angular-pipe.jade b/public/docs/ts/latest/testing/testing-an-angular-pipe.jade index 935d3d20f4..4a741a0abc 100644 --- a/public/docs/ts/latest/testing/testing-an-angular-pipe.jade +++ b/public/docs/ts/latest/testing/testing-an-angular-pipe.jade @@ -91,7 +91,7 @@ include ../_util-fns We're also assuming that you're already comfortable with basic Angular 2 concepts and the tools we introduced in the [QuickStart](../quickstart.html) and the [Tour of Heroes](../tutorial/) tutorial - such as npm, gulp, and live-server. + such as npm, gulp, and lite-server. 这一章单元测试是在其它章节的基础上写的。我们建议你按顺序阅读它们。同时,我们假设你已经对Angular 2的原理、[“快速起步”](../quickstart.html)和 [英雄指南](../tutorial)中介绍的npmgulplive-server等工具都已经有所了解。 diff --git a/public/docs/ts/latest/tutorial/toh-pt5.jade b/public/docs/ts/latest/tutorial/toh-pt5.jade index e9abf04353..40be34aecb 100644 --- a/public/docs/ts/latest/tutorial/toh-pt5.jade +++ b/public/docs/ts/latest/tutorial/toh-pt5.jade @@ -360,11 +360,11 @@ code-example(language="bash"). ### 让路由器生效 - The *Component Router* is a service. We have to import our `APP_ROUTER_PROVIDERS` which + The *Component Router* is a service. We have to import our `appRouterProviders` which contains our configured router and make it available to the application by adding it to the `bootstrap` array. - *组件路由器*是一个服务。我们得导入`APP_ROUTER_PROVIDERS`(它包含了我们配置好的路由器),并通过把它添加到`bootstrap`的数组参数中让它在此应用中可用。 + *组件路由器*是一个服务。我们得导入`appRouterProviders`(它包含了我们配置好的路由器),并通过把它添加到`bootstrap`的数组参数中让它在此应用中可用。 +makeExample('toh-5/ts/app/main.ts', '', 'app/main.ts')(format=".") diff --git a/public/news.jade b/public/news.jade index 5fb3ea3766..9f1c60923c 100644 --- a/public/news.jade +++ b/public/news.jade @@ -4,6 +4,19 @@ .clear .grid-fluid + .c6 + .article-card + .date July 11, 2016 + .title + a( + target="_blank" + href="http://angularjs.blogspot.com/2016/07/angular-in-china-and-beyond-introducing.html" + ) Angular in China and beyond: Introducing angular.cn + p We're excited to share the newly-launched angular.cn with you! Read on to learn how we did it, and how you can get involved in bringing Angular to your locale.... + + .author + img(src="/resources/images/bios/naomi.jpg") + .posted Posted by Naomi Black .c6 .article-card .date June 30, 2016 @@ -17,20 +30,6 @@ img(src="/resources/images/bios/stephenfluin.jpg") .posted Posted by Stephen Fluin - .c6 - .article-card - .date June 21, 2016 - .title - a( - target="_blank" - href="http://angularjs.blogspot.com/2016/06/rc3-now-available.html" - ) RC3 Now Available - p Today we’re happy to announce that we are shipping Angular 2.0.0-rc3. This release includes a fix for a major performance regression in RC2... - - .author - img(src="/resources/images/bios/stephenfluin.jpg") - .posted Posted by Stephen Fluin - .grid-fluid.l-space-bottom-2.l-space-top-4 .c12.text-center h3.text-headline.text-uppercase Developer Community @@ -44,7 +43,7 @@ target="_blank" href="http://www.bennadel.com/blog/3116-using-an-item-template-with-an-html-dropdown-menu-component-in-angular-2-rc-3.htm" ) Using An Item Template With An HTML Dropdown Menu Component - p A while ago, I played around with trying to create an HTML Dropdown menu component in Angular 2. I discovered that you could pass Template references into components for dynamic rendering.... + p A while ago, I played around with trying to create an HTML Dropdown menu component in Angular 2. I discovered that you could pass Template references into components for dynamic rendering... .author img(src="/resources/images/bios/shield-bio-placeholder.png") .posted Posted by Ben Nadel @@ -70,23 +69,23 @@ target="_blank" href="http://blog.mgechev.com/2016/06/26/tree-shaking-angular2-production-build-rollup-javascript/" ) Building an Angular 2 Application for Production - p During the keynote of ng-conf, the core team managed to drop the size of the “Hello world!” app to less than 50K! In this blog post we’ll explain all the steps we need to go through in order to achieve such results!. + p During the keynote of ng-conf, the core team managed to drop the size of the “Hello world!” app to less than 50K! In this blog post we’ll explain all the steps we need to go through in order to achieve such results! .author img(src="/resources/images/bios/shield-bio-placeholder.png") .posted Posted by Minko Gechev .c6 .article-card - .date June 14, 2016 + .date June 22, 2016 .title a( target="_blank" - href="http://blog.thoughtram.io/angular/2016/06/14/routing-in-angular-2-revisited.html" - ) Routing in Angular 2 Revisited - p Just recently, the Angular team announced yet another version of the new router. Take a first look at the new and better APIs, touching on the most common scenarios... + href="https://mp.weixin.qq.com/s?__biz=MzIwNjQwMzUwMQ==&mid=2247483837&idx=1&sn=932b359504eec2ae50a3bcba2964b3c2&scene=2&srcid=0622Tab8nj3W3cAkphohB8wM" + ) Why I chose Angular 2 / 我为什么选择Angular 2 (in Chinese) + p Ralph Wang, a senior developer with 18 years’ experience in China, tells why he chooses Angular 2. No choice is painful, but too many choices can be chaotic and cause even more pain... .author - img(src="/resources/images/bios/pascalprecht.jpg") - .posted Posted by Pascal Precht + img(src="/resources/images/bios/angular-gde-bio-placeholder.png") + .posted Posted by Ralph Wang .grid-fluid.l-space-bottom-2.l-space-top-4 diff --git a/scripts/deploy-install-preview.sh b/scripts/deploy-install-preview.sh new file mode 100755 index 0000000000..b4998f6717 --- /dev/null +++ b/scripts/deploy-install-preview.sh @@ -0,0 +1,6 @@ + #!/usr/bin/env bash + +set -ex -o pipefail + +./scripts/deploy-install.sh +(cd ../angular && git checkout master) \ No newline at end of file diff --git a/scripts/deploy-install.sh b/scripts/deploy-install.sh new file mode 100755 index 0000000000..38cf6c7d19 --- /dev/null +++ b/scripts/deploy-install.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -ex -o pipefail + +(cd ../ && git clone https://github.com/angular/angular.git --branch $LATEST_RELEASE) \ No newline at end of file diff --git a/scripts/examples-install-preview.sh b/scripts/examples-install-preview.sh new file mode 100755 index 0000000000..cd2beddd07 --- /dev/null +++ b/scripts/examples-install-preview.sh @@ -0,0 +1,6 @@ + #!/usr/bin/env bash + +set -ex -o pipefail + +./scripts/examples-install.sh +(cd public/docs/_examples && npm install angular/{core,common,compiler,platform-browser,platform-browser-dynamic,http,forms,router-deprecated,router,upgrade}-builds --no-optional) \ No newline at end of file diff --git a/scripts/install.sh b/scripts/examples-install.sh similarity index 91% rename from scripts/install.sh rename to scripts/examples-install.sh index 15c2e4c89d..2501ad2b8d 100755 --- a/scripts/install.sh +++ b/scripts/examples-install.sh @@ -2,7 +2,6 @@ set -ex -o pipefail -npm install --no-optional (cd public/docs/_examples && npm install --no-optional) (cd public/docs/_examples/_protractor && npm install --no-optional) npm run webdriver:update --prefix public/docs/_examples/_protractor diff --git a/tools/api-builder/angular.io-package/tag-defs/Annotation.js b/tools/api-builder/angular.io-package/tag-defs/Annotation.js new file mode 100644 index 0000000000..223195a9d7 --- /dev/null +++ b/tools/api-builder/angular.io-package/tag-defs/Annotation.js @@ -0,0 +1,4 @@ +// A ts2dart compiler annotation that can be ignored in API docs. +module.exports = function() { + return { name: 'Annotation', ignore: true }; +}; diff --git a/tools/api-builder/angular.io-package/tag-defs/index.js b/tools/api-builder/angular.io-package/tag-defs/index.js index 782a09189e..bbc9f5d167 100644 --- a/tools/api-builder/angular.io-package/tag-defs/index.js +++ b/tools/api-builder/angular.io-package/tag-defs/index.js @@ -1,9 +1,11 @@ module.exports = [ + require('./Annotation'), require('./deprecated'), require('./howToUse'), require('./whatItDoes'), require('./internal'), require('./stable'), + require('./ts2dart_const'), require('./experimental'), require('./docsNotRequired'), require('./security'), diff --git a/tools/api-builder/angular.io-package/tag-defs/ts2dart_const.js b/tools/api-builder/angular.io-package/tag-defs/ts2dart_const.js new file mode 100644 index 0000000000..dc2c3fa401 --- /dev/null +++ b/tools/api-builder/angular.io-package/tag-defs/ts2dart_const.js @@ -0,0 +1,4 @@ +// A ts2dart compiler annotation that can be ignored in API docs. +module.exports = function() { + return { name: 'ts2dart_const', ignore: true }; +}; diff --git a/tools/api-builder/docs-package/processors/addNotYetDocumentedProperty.js b/tools/api-builder/docs-package/processors/addNotYetDocumentedProperty.js index ed3b29b22b..e4e2c0540d 100644 --- a/tools/api-builder/docs-package/processors/addNotYetDocumentedProperty.js +++ b/tools/api-builder/docs-package/processors/addNotYetDocumentedProperty.js @@ -23,8 +23,7 @@ module.exports = function addNotYetDocumentedProperty(EXPORT_DOC_TYPES, log, cre } if (doc.notYetDocumented) { - // TODO: (ericjim) should I remove this? - log.warn(createDocMessage("Not yet documented", doc)); + log.info(createDocMessage("Not yet documented", doc)); } }); @@ -35,4 +34,4 @@ module.exports = function addNotYetDocumentedProperty(EXPORT_DOC_TYPES, log, cre function notYetDocumented(doc) { return !doc.noDescription && doc.description.trim().length == 0; -} \ No newline at end of file +} diff --git a/tools/api-builder/links-package/index.js b/tools/api-builder/links-package/index.js index 3036c2441b..4d4a06c82b 100644 --- a/tools/api-builder/links-package/index.js +++ b/tools/api-builder/links-package/index.js @@ -3,18 +3,25 @@ var Package = require('dgeni').Package; module.exports = new Package('links', []) .factory(require('./inline-tag-defs/link')) -.factory(require('./inline-tag-defs/linkDevGuide')) +.factory(require('./inline-tag-defs/linkDocs')) .factory(require('./inline-tag-defs/example')) .factory(require('./inline-tag-defs/exampleTabs')) .factory(require('dgeni-packages/links/services/getAliases')) .factory(require('dgeni-packages/links/services/getDocFromAlias')) .factory(require('./services/getLinkInfo')) .factory(require('./services/parseArgString')) +.factory(require('./services/moduleScopeLinkDisambiguator')) +.factory(require('./services/deprecatedDocsLinkDisambiguator')) .factory(require('./services/getApiFragmentFileName')) -.config(function(inlineTagProcessor, linkInlineTagDef, linkDevGuideInlineTagDef, exampleInlineTagDef, exampleTabsInlineTagDef) { +.config(function(inlineTagProcessor, linkInlineTagDef, linkDocsInlineTagDef, exampleInlineTagDef, exampleTabsInlineTagDef) { inlineTagProcessor.inlineTagDefinitions.push(linkInlineTagDef); - inlineTagProcessor.inlineTagDefinitions.push(linkDevGuideInlineTagDef); + inlineTagProcessor.inlineTagDefinitions.push(linkDocsInlineTagDef); inlineTagProcessor.inlineTagDefinitions.push(exampleInlineTagDef); inlineTagProcessor.inlineTagDefinitions.push(exampleTabsInlineTagDef); +}) + +.config(function(getLinkInfo, moduleScopeLinkDisambiguator, deprecatedDocsLinkDisambiguator) { + getLinkInfo.disambiguators.push(moduleScopeLinkDisambiguator); + getLinkInfo.disambiguators.push(deprecatedDocsLinkDisambiguator); }); diff --git a/tools/api-builder/links-package/inline-tag-defs/example.js b/tools/api-builder/links-package/inline-tag-defs/example.js index caaccfd61f..1df3f0223f 100644 --- a/tools/api-builder/links-package/inline-tag-defs/example.js +++ b/tools/api-builder/links-package/inline-tag-defs/example.js @@ -40,18 +40,8 @@ module.exports = function exampleInlineTagDef(getLinkInfo, parseArgString, getAp }; }; -// Examples of what @example and @exampleTabs markup looks like in the angular/angular source. -//* -//* {@example core/application_spec.ts hello-app -title='Sample component' } -//* -//* {@exampleTabs core/application_spec.ts,core/application_spec.ts "hello-app,hello-app2" -titles="Hello app1, Hello app2" } -//* - - function quote(str) { if (str == null || str.length === 0) return str; str = str.replace("'","'\'"); return "'" + str + "'"; } - - diff --git a/tools/api-builder/links-package/inline-tag-defs/linkDevGuide.js b/tools/api-builder/links-package/inline-tag-defs/linkDevGuide.js deleted file mode 100644 index 1e40227e81..0000000000 --- a/tools/api-builder/links-package/inline-tag-defs/linkDevGuide.js +++ /dev/null @@ -1,53 +0,0 @@ -var path = require('canonical-path'); -var fs = require("fs"); -var jsonFile = require('jsonfile'); - -var INLINE_LINK = /(\S+)(?:\s+([\s\S]+))?/; - -/** - * @dgService linkDevGuideInlineTagDef - * @description - * Process inline link tags (of the form {@linkDevGuide some/uri 'Some Title'}), replacing them with HTML anchors. - * The uri should point to a jade page in the DevGuide without the .jade extension ( under public/docs ). - * If the title is omitted an attempt will be made to determine the title of the jade page being pointed to. If not found - * the the title will simply be the last part of the link. - * Examples - * {@linkDevGuide ts/latest/guide/gettingStarted } - * {@linkDevGuide js/latest/guide/gettingStarted 'Javascript version of getting started' } - * {@linkDevGuide ts/latest/guide/gettingStarted title="Typescript version of getting started" } - * @kind function - */ -module.exports = function linkDevGuideInlineTagDef(parseArgString, createDocMessage, log) { - return { - name: 'linkDevGuide', - description: 'Process inline link tags (of the form {@link some/uri "Some Title"}), replacing them with HTML anchors', - handler: function(doc, tagName, tagDescription) { - - // Parse out the uri and title - var tagArgs = parseArgString(tagDescription); - var unnamedArgs = tagArgs._; - var uri = unnamedArgs[0]; - var title = tagArgs.title || (unnamedArgs.length > 1 ? unnamedArgs[1] : null); - - var jadePath = path.join('./public/docs', uri + '.jade'); - var key = path.basename(jadePath, '.jade'); - if ( !fs.existsSync(jadePath)) { - log.warn(createDocMessage('Invalid DevGuide example (unable to locate jade file: "' + jadePath + '")', doc)); - } else { - if (!title) { - var jsonFilePath = path.join(path.dirname(jadePath), '_data.json'); - if ( fs.existsSync(jsonFilePath)) { - var jsonObj = jsonFile.readFileSync(jsonFilePath); - title = jsonObj[key] && jsonObj[key].title; - } - } - } - var url = path.join('/docs', uri + '.html'); - title = title || key || url; - - return "" + title + ""; - - } - }; -}; - diff --git a/tools/api-builder/links-package/inline-tag-defs/linkDocs.js b/tools/api-builder/links-package/inline-tag-defs/linkDocs.js new file mode 100644 index 0000000000..6254fc2d2f --- /dev/null +++ b/tools/api-builder/links-package/inline-tag-defs/linkDocs.js @@ -0,0 +1,75 @@ +var path = require('canonical-path'); +var fs = require("fs"); +var jsonFile = require('jsonfile'); + +/** + * @dgService linkDocsInlineTagDef + * @description + * Process inline link tags (of the form {@linkDocs some/uri 'Some Title'}), replacing them with HTML anchors. + * The uri should point to a jade page in the Docs without the .jade extension ( under public/docs ). + * If the title is omitted an attempt will be made to determine the title of the jade page being pointed to. If not found + * the the title will simply be the last part of the link. + * Examples + * {@linkDocs guide/gettingStarted 'QuickStart'} + * {@linkDocs ts/latest/guide/quickstart } + * {@linkDocs js/latest/guide/quickstart 'Javascript version of getting started' } + * {@linkDocs ts/latest/guide/quickstart title="Typescript version of getting started" } + * @kind function + * @property {string} lang Default docs API page language when not explicitly given in URI; one of ts|js|dart. + * @property {string} vers Default docs version. Currently only 'latest'. + */ +module.exports = function linkDocsInlineTagDef(parseArgString, createDocMessage, log) { + var _self = { + name: 'linkDocs', + lang: 'ts', + vers: 'latest', + description: 'Process inline link tags (of the form {@linkDocs some/uri [title=]"Some Title"}), replacing them with HTML anchors', + + handler: function(doc, tagName, tagDescription) { + // Parse out the uri and title + var tagArgs = parseArgString(tagDescription); + var unnamedArgs = tagArgs._; + var uri = unnamedArgs[0]; + var title = tagArgs.title || (unnamedArgs.length > 1 ? unnamedArgs[1] : null); + + // Are there parameters and/or an anchor? + var matches, paramAnchor = ''; + if (matches = uri.match(/([^\#\?]*)([\#\?].*)/)) { + uri = matches[1]; + paramAnchor = matches[2]; + } + + // Is this a chapter-relative uri like 'guide/...'? + if (!uri.match(/^(ts|js|dart)/)) { + var lang = _self.lang; + var vers = _self.vers; + var prevUri = uri; + uri = path.join(lang, vers, uri); + log.info('Ajusted linkDocs chapter-relative uri (' + doc.fileInfo.baseName + '): ' + prevUri + ' -> ' + uri); + } + + var isValid = false; + var jadePath = path.join('./public/docs', uri + '.jade'); + var key = path.basename(jadePath, '.jade'); + if ( !fs.existsSync(jadePath)) { + log.warn(createDocMessage('Invalid docs link (unable to locate jade file: "' + jadePath + '")', doc)); + } else { + isValid = true; + if (!title) { + var jsonFilePath = path.join(path.dirname(jadePath), '_data.json'); + if ( fs.existsSync(jsonFilePath)) { + var jsonObj = jsonFile.readFileSync(jsonFilePath); + title = jsonObj[key] && jsonObj[key].title; + } + } + } + var url = path.join('/docs', uri + '.html' + paramAnchor); + title = title || key || url; + + return isValid ? + '' + title + '' : + '' + title + ''; + } + }; + return _self; +}; diff --git a/tools/api-builder/links-package/services/deprecatedDocsLinkDisambiguator.js b/tools/api-builder/links-package/services/deprecatedDocsLinkDisambiguator.js new file mode 100644 index 0000000000..3db79ffbd6 --- /dev/null +++ b/tools/api-builder/links-package/services/deprecatedDocsLinkDisambiguator.js @@ -0,0 +1,13 @@ +var _ = require('lodash'); + +module.exports = function deprecatedDocsLinkDisambiguator() { + return function(url, title, currentDoc, docs) { + if (docs.length != 2) return docs; + + var filteredDocs = _.filter(docs, function(doc) { + return !doc.fileInfo.relativePath.match(/\/(\w+)-deprecated\//); + }); + + return filteredDocs.length > 0 ? filteredDocs : docs; + }; +}; diff --git a/tools/api-builder/links-package/services/getLinkInfo.js b/tools/api-builder/links-package/services/getLinkInfo.js index a180984c69..c0dad0025a 100644 --- a/tools/api-builder/links-package/services/getLinkInfo.js +++ b/tools/api-builder/links-package/services/getLinkInfo.js @@ -10,10 +10,17 @@ var path = require('canonical-path'); * @return {Object} The link information * * @property {boolean} relativeLinks Whether we expect the links to be relative to the originating doc + * @property {array 1 ) { linkInfo.valid = false; @@ -68,4 +80,5 @@ module.exports = function getLinkInfo(getDocFromAlias, encodeCodeBlock, log) { return linkInfo; }; + }; \ No newline at end of file diff --git a/tools/api-builder/links-package/services/moduleScopeLinkDisambiguator.js b/tools/api-builder/links-package/services/moduleScopeLinkDisambiguator.js new file mode 100644 index 0000000000..ec1758fe99 --- /dev/null +++ b/tools/api-builder/links-package/services/moduleScopeLinkDisambiguator.js @@ -0,0 +1,15 @@ +var _ = require('lodash'); + +module.exports = function moduleScopeLinkDisambiguator() { + return function(url, title, currentDoc, docs) { + if (docs.length > 1) { + // filter out target docs that are not in the same module as the source doc + var filteredDocs = _.filter(docs, function(doc) { + return doc.moduleDoc === currentDoc.moduleDoc; + }); + // if all target docs are in a different module then just return the full collection of ambiguous docs + return filteredDocs.length > 0 ? filteredDocs : docs; + } + return docs; + }; +}; diff --git a/tools/doc-shredder/processors/shredMapProcessor.js b/tools/doc-shredder/processors/shredMapProcessor.js index 5eb9ac1b24..569ba5511d 100644 --- a/tools/doc-shredder/processors/shredMapProcessor.js +++ b/tools/doc-shredder/processors/shredMapProcessor.js @@ -18,25 +18,37 @@ module.exports = function shredMapProcessor(log, createDocMessage) { var fragToJadeMap = {}; docs.forEach(function(doc) { - var jadePath = path.join(options.jadeDir, doc.fileInfo.relativePath); + var relativePath = doc.fileInfo.relativePath; + var jadePath = path.join(options.jadeDir, relativePath); + var lang = relativePath.substr(0, relativePath.indexOf('\/')); + var appProjDirName = jadeBaseFileNameToExampleName(doc.fileInfo.baseName); var fragInfoSet = {}; doc.fragItems.forEach(function(fragItem) { var mixinPath = fragItem.mixinPath; var fullExamplePath; + // Normalize mixinPath: strip out optional trailing '(...)' + var mixinPath = mixinPath.replace(/ \([^\)]*\)/,''); if ( mixinPath.indexOf('_api') >= 0) { var sourcePath = mixinPath.replace('_api/',''); fullExamplePath = path.join(options.apiExamplesDir, sourcePath); } else { fullExamplePath = path.join(options.devguideExamplesDir, mixinPath); } - var region = fragItem.region ? "-" + fragItem.region : ''; - var extn = path.extname(mixinPath); - var basename = path.basename(mixinPath, extn); - var fragDir = path.dirname(mixinPath); - var fragPath = path.join(fragDir, basename + region + extn) + '.md'; - var fullFragPath = path.join(options.fragmentsDir, fragPath); - - var fragInfo = { fragPath: fullFragPath, examplePath: fullExamplePath, exists: fs.existsSync(fullFragPath) }; + var fragInfo = makeFragInfo(options.fragmentsDir, fullExamplePath, fragItem, mixinPath); + if (!fragInfo.exists) { + var savedFragInfo = fragInfo; + // Assume that mixinPath is actually app-project-folder relative and + // prepend "lang/appProjDirName": + var appProjRelPath = mixinPath; + mixinPath = appProjDirName + '/' + lang + '/' + mixinPath; + fragInfo = makeFragInfo(options.fragmentsDir, fullExamplePath, fragItem, mixinPath); + if (fragInfo.exists) { + log.info('Ajusted example path (' + doc.fileInfo.baseName + '): ' + appProjRelPath + ' -> ' + mixinPath); + } else { + fragInfo = savedFragInfo; + } + } + var fragPath = fragInfo.relFragPath; fragInfoSet[fragPath] = fragInfo; if (fragInfo.exists) { var jadePathsSet = fragToJadeMap[fragPath]; @@ -46,7 +58,7 @@ module.exports = function shredMapProcessor(log, createDocMessage) { } jadePathsSet[jadePath] = jadePath; } else { - var relativePath = path.relative(".", fullFragPath); + var relativePath = path.relative(".", fragInfo.fragPath); log.warn(createDocMessage('Invalid example (unable to locate fragment file: "' + relativePath + '")', doc)); } }); @@ -82,13 +94,24 @@ module.exports = function shredMapProcessor(log, createDocMessage) { } }; -function getExampleName(fragPath) { - // pattern to isolate base fileName and extension from fragment name - var rx = /(.*)\-(.*)\.(.s)/; - var r = rx.exec(fragPath); - if (r) { - return r[1] + '.' + r[3]; - } else { - return fragPath; - } +// TODO: use the functionality in public/resources/js/util.js once it lands. +function jadeBaseFileNameToExampleName(name) { + // Adjust for known cases where chapter name is not the example name. + var matches = name.match(/(toh-)pt(\d+)/); + if (matches) name = matches[1] + matches[2]; + return name; +} + +function makeFragInfo(fragmentsDir, fullExamplePath, fragItem, mixinPath) { + var region = fragItem.region ? "-" + fragItem.region : ''; + var extn = path.extname(mixinPath); + var basename = path.basename(mixinPath, extn); + var fragDir = path.dirname(mixinPath); + var fragPath = path.join(fragDir, basename + region + extn) + '.md'; + var fullFragPath = path.join(fragmentsDir, fragPath); + return { + fragPath: fullFragPath, + relFragPath: fragPath, + examplePath: fullExamplePath, + exists: fs.existsSync(fullFragPath) }; } \ No newline at end of file diff --git a/tools/plunker-builder/plunkerBuilder.js b/tools/plunker-builder/plunkerBuilder.js index 7318a01857..588341d241 100644 --- a/tools/plunker-builder/plunkerBuilder.js +++ b/tools/plunker-builder/plunkerBuilder.js @@ -9,7 +9,7 @@ var mkdirp = require('mkdirp'); var indexHtmlTranslator = require('./indexHtmlTranslator'); var regionExtractor = require('../doc-shredder/regionExtractor'); -var COPYRIGHT, COPYRIGHT_JS, COPYRIGHT_HTML; +var COPYRIGHT, COPYRIGHT_JS_CSS, COPYRIGHT_HTML; var SYSTEMJS_CONFIG; // content of systemjs.config.js for plunkers that use systemjs var TSCONFIG; // content of tsconfig.json for plunkers that use systemjs @@ -32,11 +32,8 @@ function buildCopyrightStrings() { function buildPlunkers(basePath, destPath, options) { getSystemJsConfigPlunker(basePath); var errFn = options.errFn || function(e) { console.log(e); }; - var configExtns = ['plnkr.json', '*plnkr.json']; - var gpaths = configExtns.map(function(extn) { - return path.join(basePath, '**/' + extn); - }); - var fileNames = globby.sync(gpaths, { ignore: "**/node_modules/**"}); + var plunkerPaths = path.join(basePath, '**/*plnkr.json'); + var fileNames = globby.sync(plunkerPaths, { ignore: "**/node_modules/**"}); fileNames.forEach(function(configFileName) { try { buildPlunkerFrom(configFileName, basePath, destPath); @@ -51,7 +48,7 @@ function buildPlunkers(basePath, destPath, options) { // description: optional string - description of this plunker - defaults to the title in the index.html page. // tags: [] - optional array of strings // main: string - filename of what will become index.html in the plunker - defaults to index.html -function buildPlunkerFrom(configFileName, basePath, destPath ) { +function buildPlunkerFrom(configFileName, basePath, destPath) { // replace ending 'plnkr.json' with 'plnkr.no-link.html' to create output file name; var outputFileName = configFileName.substr(0, configFileName.length - 'plnkr.json'.length) + 'plnkr.no-link.html'; var altFileName; @@ -90,8 +87,7 @@ function buildPlunkerFrom(configFileName, basePath, destPath ) { function addSystemJsConfig(config, postData){ if (config.basePath.indexOf('/ts') > -1) { // uses systemjs.config.js so add plunker version - var relativeFileName = 'systemjs.config.js'; - postData['files[' + relativeFileName + ']'] = SYSTEMJS_CONFIG; + postData['files[systemjs.config.js]'] = SYSTEMJS_CONFIG; postData['files[tsconfig.json]'] = TSCONFIG; } }