Merge remote-tracking branch 'origin/master' into aio
# Conflicts: # aio/content/guide/aot-compiler.md # aio/content/guide/change-log.md # aio/content/guide/ts-to-js.md # aio/content/marketing/docs.md # aio/content/navigation.json # aio/src/app/documents/document.service.ts
This commit is contained in:
commit
de34171fa2
127
CHANGELOG.md
127
CHANGELOG.md
|
@ -1,3 +1,127 @@
|
|||
<a name="5.0.0-beta.4"></a>
|
||||
# [5.0.0-beta.4](https://github.com/angular/angular/compare/5.0.0-beta.3...5.0.0-beta.4) (2017-08-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **aio:** fix compilation error by using the correct type for `providers` ([4d523fd](https://github.com/angular/angular/commit/4d523fd))
|
||||
* **aio:** skip PWA test when redeploying non-public commit ([06faac8](https://github.com/angular/angular/commit/06faac8))
|
||||
* **compiler:** Don't strip CSS source maps ([64b4be9](https://github.com/angular/angular/commit/64b4be9))
|
||||
* **forms:** re-assigning options should not clear select ([32ff21c](https://github.com/angular/angular/commit/32ff21c)), closes [#18330](https://github.com/angular/angular/issues/18330)
|
||||
* **language-service:** remove tsickle dependency ([bc22ff1](https://github.com/angular/angular/commit/bc22ff1))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **common:** mark NgTemplateOutlet API as stable ([0a73e8d](https://github.com/angular/angular/commit/0a73e8d))
|
||||
* **forms:** add status to `AbstractControlDirective` ([233ef93](https://github.com/angular/angular/commit/233ef93))
|
||||
* **forms:** add updateOn support to ngModelOptions ([1cfa79c](https://github.com/angular/angular/commit/1cfa79c))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **aio:** update to new version of build-optimizer ([088532b](https://github.com/angular/angular/commit/088532b))
|
||||
* **core:** add option to remove blank text nodes from compiled templates ([d2c0d98](https://github.com/angular/angular/commit/d2c0d98))
|
||||
* **core:** Remove decorator DSL which depends on Reflect ([cac130e](https://github.com/angular/angular/commit/cac130e))
|
||||
|
||||
|
||||
|
||||
<a name="4.3.5"></a>
|
||||
## [4.3.5](https://github.com/angular/angular/compare/4.3.4...4.3.5) (2017-08-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **aio:** skip PWA test when redeploying non-public commit ([b9c1c91](https://github.com/angular/angular/commit/b9c1c91))
|
||||
* **core:** forbid destroyed views to be inserted or moved in VC ([972538b](https://github.com/angular/angular/commit/972538b)), closes [#18615](https://github.com/angular/angular/issues/18615)
|
||||
* **forms:** re-assigning options should not clear select ([a1624f2](https://github.com/angular/angular/commit/a1624f2)), closes [#18330](https://github.com/angular/angular/issues/18330)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **aio:** update to new version of build-optimizer ([d7be4f1](https://github.com/angular/angular/commit/d7be4f1))
|
||||
|
||||
|
||||
|
||||
<a name="4.3.4"></a>
|
||||
## [4.3.4](https://github.com/angular/angular/compare/4.3.3...4.3.4) (2017-08-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** revert container/queried animations accordingly during cancel ([#18516](https://github.com/angular/angular/issues/18516)) ([5a165eb](https://github.com/angular/angular/commit/5a165eb))
|
||||
* **animations:** support persisting dynamic styles within animation states ([#18468](https://github.com/angular/angular/issues/18468)) ([e0660b1](https://github.com/angular/angular/commit/e0660b1)), closes [#18423](https://github.com/angular/angular/issues/18423) [#17505](https://github.com/angular/angular/issues/17505)
|
||||
* **benchpress:** compile cleanly with TS 2.4 ([#18455](https://github.com/angular/angular/issues/18455)) ([5afc7ab](https://github.com/angular/angular/commit/5afc7ab))
|
||||
* **compiler:** cleanly compile with TypeScript 2.4 ([#18456](https://github.com/angular/angular/issues/18456)) ([5e4054b](https://github.com/angular/angular/commit/5e4054b))
|
||||
* **compiler:** ignore [@import](https://github.com/import) in multi-line css ([#18452](https://github.com/angular/angular/issues/18452)) ([e7e7622](https://github.com/angular/angular/commit/e7e7622)), closes [#18038](https://github.com/angular/angular/issues/18038)
|
||||
|
||||
|
||||
<a name="5.0.0-beta.3"></a>
|
||||
# [5.0.0-beta.3](https://github.com/angular/angular/compare/5.0.0-beta.2...5.0.0-beta.3) (2017-08-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** revert container/queried animations accordingly during cancel ([#18516](https://github.com/angular/angular/issues/18516)) ([c0c03dc](https://github.com/angular/angular/commit/c0c03dc))
|
||||
* **animations:** support persisting dynamic styles within animation states ([#18468](https://github.com/angular/angular/issues/18468)) ([05472cb](https://github.com/angular/angular/commit/05472cb)), closes [#18423](https://github.com/angular/angular/issues/18423) [#17505](https://github.com/angular/angular/issues/17505)
|
||||
* **benchpress:** compile cleanly with TS 2.4 ([#18455](https://github.com/angular/angular/issues/18455)) ([e25b3dd](https://github.com/angular/angular/commit/e25b3dd))
|
||||
* **common:** don't recreate view when context shape doesn't change ([#18277](https://github.com/angular/angular/issues/18277)) ([685cc26](https://github.com/angular/angular/commit/685cc26)), closes [#13407](https://github.com/angular/angular/issues/13407)
|
||||
* **compiler:** cleanly compile with TypeScript 2.4 ([#18456](https://github.com/angular/angular/issues/18456)) ([7c47b62](https://github.com/angular/angular/commit/7c47b62))
|
||||
* **compiler:** ignore [@import](https://github.com/import) in multi-line css ([#18452](https://github.com/angular/angular/issues/18452)) ([1dca575](https://github.com/angular/angular/commit/1dca575)), closes [#18038](https://github.com/angular/angular/issues/18038)
|
||||
* **compiler-cli:** disable buggy expression lowering ([#18513](https://github.com/angular/angular/issues/18513)) ([ca695e0](https://github.com/angular/angular/commit/ca695e0))
|
||||
* **compiler-cli:** fix and re-enable expression lowering ([#18570](https://github.com/angular/angular/issues/18570)) ([6f2038c](https://github.com/angular/angular/commit/6f2038c)), closes [#18388](https://github.com/angular/angular/issues/18388)
|
||||
* **compiler-cli:** modified ngc to throw all errors, not just syntax ([#18388](https://github.com/angular/angular/issues/18388)) ([5651e4a](https://github.com/angular/angular/commit/5651e4a))
|
||||
* **compiler-cli:** remove minimist dependency of compiler-cli/index ([#18532](https://github.com/angular/angular/issues/18532)) ([5b7432b](https://github.com/angular/angular/commit/5b7432b))
|
||||
* **core:** fix platform-browser-dynamic ([#18576](https://github.com/angular/angular/issues/18576)) ([f0a5501](https://github.com/angular/angular/commit/f0a5501))
|
||||
* **core:** forbid destroyed views to be inserted or moved in VC ([#18568](https://github.com/angular/angular/issues/18568)) ([e54bd59](https://github.com/angular/angular/commit/e54bd59)), closes [#17780](https://github.com/angular/angular/issues/17780)
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** Create StaticInjector which does not depend on Reflect polyfill. ([d9d00bd](https://github.com/angular/angular/commit/d9d00bd))
|
||||
* **forms:** add default updateOn values for groups and arrays ([#18536](https://github.com/angular/angular/issues/18536)) ([ff5c58b](https://github.com/angular/angular/commit/ff5c58b))
|
||||
* **forms:** add updateOn blur option to FormControls ([#18408](https://github.com/angular/angular/issues/18408)) ([333a708](https://github.com/angular/angular/commit/333a708)), closes [#7113](https://github.com/angular/angular/issues/7113)
|
||||
* **forms:** add updateOn submit option to FormControls ([#18514](https://github.com/angular/angular/issues/18514)) ([f69561b](https://github.com/angular/angular/commit/f69561b))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* switch angular to use StaticInjector instead of ReflectiveInjector ([fcadbf4](https://github.com/angular/angular/commit/fcadbf4)), closes [#18496](https://github.com/angular/angular/issues/18496)
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* `platformXXXX()` no longer accepts providers which depend on reflection.
|
||||
Specifically the method signature when from `Provider[]` to
|
||||
`StaticProvider[]`.
|
||||
|
||||
Example:
|
||||
Before:
|
||||
```
|
||||
[
|
||||
MyClass,
|
||||
{provide: ClassA, useClass: SubClassA}
|
||||
]
|
||||
|
||||
```
|
||||
|
||||
After:
|
||||
```
|
||||
[
|
||||
{provide: MyClass, deps: [Dep1,...]},
|
||||
{provide: ClassA, useClass: SubClassA, deps: [Dep1,...]}
|
||||
]
|
||||
```
|
||||
|
||||
NOTE: This only applies to platform creation and providers for the JIT
|
||||
compiler. It does not apply to `@Component` or `@NgModule` provides
|
||||
declarations.
|
||||
|
||||
Benchpress note: Previously Benchpress also supported reflective
|
||||
provides, which now require static providers.
|
||||
|
||||
DEPRECATION:
|
||||
|
||||
- `ReflectiveInjector` is now deprecated as it will be remove. Use
|
||||
`Injector.create` as a replacement.
|
||||
|
||||
<a name="5.0.0-beta.2"></a>
|
||||
# [5.0.0-beta.2](https://github.com/angular/angular/compare/5.0.0-beta.1...5.0.0-beta.2) (2017-08-02)
|
||||
|
||||
|
@ -14,17 +138,14 @@
|
|||
* **router:** add events tracking activation of individual routes ([49cd851](https://github.com/angular/angular/commit/49cd851))
|
||||
|
||||
|
||||
|
||||
<a name="4.3.3"></a>
|
||||
## [4.3.3](https://github.com/angular/angular/compare/4.3.2...4.3.3) (2017-08-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** fix for element needing implicit parent placed in top-level ng-container ([f5cbc2e](https://github.com/angular/angular/commit/f5cbc2e)), closes [#18314](https://github.com/angular/angular/issues/18314)
|
||||
|
||||
|
||||
|
||||
<a name="5.0.0-beta.1"></a>
|
||||
# [5.0.0-beta.1](https://github.com/angular/angular/compare/5.0.0-beta.0...5.0.0-beta.1) (2017-07-27)
|
||||
|
||||
|
|
|
@ -32,7 +32,8 @@ export class BuildCreator extends EventEmitter {
|
|||
then(() => Promise.all([this.exists(prDir), this.exists(shaDir)])).
|
||||
then(([prDirExisted, shaDirExisted]) => {
|
||||
if (shaDirExisted) {
|
||||
throw new UploadError(409, `Request to overwrite existing directory: ${shaDir}`);
|
||||
const publicOrNot = isPublic ? 'public' : 'non-public';
|
||||
throw new UploadError(409, `Request to overwrite existing ${publicOrNot} directory: ${shaDir}`);
|
||||
}
|
||||
|
||||
dirToRemoveOnError = prDirExisted ? shaDir : prDir;
|
||||
|
|
|
@ -110,6 +110,7 @@ describe('upload-server (on HTTP)', () => {
|
|||
const authorizationHeader2 = isPublic ?
|
||||
authorizationHeader : `--header "Authorization: ${c.BV_verify_verifiedNotTrusted}"`;
|
||||
const cmdPrefix = curl('', `${authorizationHeader2} ${xFileHeader}`);
|
||||
const overwriteRe = RegExp(`^Request to overwrite existing ${isPublic ? 'public' : 'non-public'} directory`);
|
||||
|
||||
|
||||
it('should not overwrite existing builds', done => {
|
||||
|
@ -120,7 +121,7 @@ describe('upload-server (on HTTP)', () => {
|
|||
expect(h.readBuildFile(pr, sha9, 'index.html', isPublic)).toBe('My content');
|
||||
|
||||
h.runCmd(`${cmdPrefix} http://${host}/create-build/${pr}/${sha9}`).
|
||||
then(h.verifyResponse(409, /^Request to overwrite existing directory/)).
|
||||
then(h.verifyResponse(409, overwriteRe)).
|
||||
then(() => expect(h.readBuildFile(pr, sha9, 'index.html', isPublic)).toBe('My content')).
|
||||
then(done);
|
||||
});
|
||||
|
@ -141,7 +142,7 @@ describe('upload-server (on HTTP)', () => {
|
|||
expect(h.readBuildFile(pr, sha9, 'index.html', isPublic)).toBe('My content');
|
||||
|
||||
h.runCmd(`${cmdPrefix} http://${host}/create-build/${pr}/${sha9Almost}`).
|
||||
then(h.verifyResponse(409, /^Request to overwrite existing directory/)).
|
||||
then(h.verifyResponse(409, overwriteRe)).
|
||||
then(() => expect(h.readBuildFile(pr, sha9, 'index.html', isPublic)).toBe('My content')).
|
||||
then(done);
|
||||
});
|
||||
|
@ -310,7 +311,7 @@ describe('upload-server (on HTTP)', () => {
|
|||
expect(h.buildExists(pr, sha0, isPublic)).toBe(false);
|
||||
|
||||
uploadBuild(sha0).
|
||||
then(h.verifyResponse(409, /^Request to overwrite existing directory/)).
|
||||
then(h.verifyResponse(409, overwriteRe)).
|
||||
then(() => {
|
||||
checkPrVisibility(isPublic);
|
||||
expect(h.readBuildFile(pr, sha0, 'index.html', isPublic)).toContain(pr);
|
||||
|
|
|
@ -153,7 +153,8 @@ describe('BuildCreator', () => {
|
|||
it('should abort and skip further operations if the build does already exist', done => {
|
||||
existsValues[shaDir] = true;
|
||||
bc.create(pr, sha, archive, isPublic).catch(err => {
|
||||
expectToBeUploadError(err, 409, `Request to overwrite existing directory: ${shaDir}`);
|
||||
const publicOrNot = isPublic ? 'public' : 'non-public';
|
||||
expectToBeUploadError(err, 409, `Request to overwrite existing ${publicOrNot} directory: ${shaDir}`);
|
||||
expect(shellMkdirSpy).not.toHaveBeenCalled();
|
||||
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||
|
@ -169,7 +170,8 @@ describe('BuildCreator', () => {
|
|||
expect(bcExistsSpy(shaDir)).toBe(false);
|
||||
|
||||
bc.create(pr, sha, archive, isPublic).catch(err => {
|
||||
expectToBeUploadError(err, 409, `Request to overwrite existing directory: ${shaDir}`);
|
||||
const publicOrNot = isPublic ? 'public' : 'non-public';
|
||||
expectToBeUploadError(err, 409, `Request to overwrite existing ${publicOrNot} directory: ${shaDir}`);
|
||||
expect(shellMkdirSpy).not.toHaveBeenCalled();
|
||||
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"interface-name": [true, "never-prefix"],
|
||||
"max-classes-per-file": [true, 4],
|
||||
"no-consecutive-blank-lines": [true, 2],
|
||||
"no-console": false,
|
||||
"no-console": [false],
|
||||
"no-namespace": [true, "allow-declarations"],
|
||||
"no-string-literal": false,
|
||||
"quotemark": [true, "single"],
|
||||
|
|
|
@ -46,8 +46,8 @@ with a bried explanation of what they mean:
|
|||
Request method other than POST.
|
||||
|
||||
- **409 (Conflict)**:
|
||||
Request to overwrite existing directory (e.g. deploy existing build or change PR visibility when
|
||||
the destination directory does already exist).
|
||||
Request to overwrite existing (public or non-public) directory (e.g. deploy existing build or
|
||||
change PR visibility when the destination directory does already exist).
|
||||
|
||||
- **413 (Payload Too Large)**:
|
||||
Payload larger than size specified in `AIO_UPLOAD_MAX_SIZE`.
|
||||
|
@ -71,7 +71,8 @@ with a bried explanation of what they mean:
|
|||
Request method other than POST.
|
||||
|
||||
- **409 (Conflict)**:
|
||||
Request to overwrite existing directory (i.e. directories for both visibilities exist).
|
||||
Request to overwrite existing (public or non-public) directory (i.e. directories for both
|
||||
visibilities exist).
|
||||
(Normally, this should not happen.)
|
||||
|
||||
|
||||
|
|
|
@ -55,10 +55,6 @@ dist/
|
|||
!testing/src/browser-test-shim.js
|
||||
!testing/karma*.js
|
||||
|
||||
# TS to JS
|
||||
!ts-to-js/js*/**/*.js
|
||||
ts-to-js/js*/**/system*.js
|
||||
|
||||
# webpack
|
||||
!webpack/**/config/*.js
|
||||
!webpack/**/*webpack*.js
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
// #docplaster
|
||||
// #docregion without-missing-translation
|
||||
import { TRANSLATIONS, TRANSLATIONS_FORMAT, LOCALE_ID, MissingTranslationStrategy } from '@angular/core';
|
||||
import { TRANSLATIONS, TRANSLATIONS_FORMAT, LOCALE_ID, MissingTranslationStrategy, StaticProvider } from '@angular/core';
|
||||
import { CompilerConfig } from '@angular/compiler';
|
||||
|
||||
export function getTranslationProviders(): Promise<Object[]> {
|
||||
export function getTranslationProviders(): Promise<StaticProvider[]> {
|
||||
|
||||
// Get the locale id from the global
|
||||
const locale = document['locale'] as string;
|
||||
|
||||
// return no providers if fail to get translation file for locale
|
||||
const noProviders: Object[] = [];
|
||||
const noProviders: StaticProvider[] = [];
|
||||
|
||||
// No locale or U.S. English: no translation providers
|
||||
if (!locale || locale === 'en-US') {
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
'use strict'; // necessary for es6 output in node
|
||||
|
||||
import { browser, element, by } from 'protractor';
|
||||
|
||||
describe('TypeScript to Javascript tests', function () {
|
||||
|
||||
beforeAll(function () {
|
||||
browser.get('');
|
||||
});
|
||||
|
||||
it('should display the basic component example', function () {
|
||||
testTag('hero-view', 'Hero Detail: Windstorm');
|
||||
});
|
||||
|
||||
it('should display the component example with lifecycle methods', function () {
|
||||
testTag('hero-lifecycle', 'Hero: Windstorm');
|
||||
});
|
||||
|
||||
it('should display component with DI example', function () {
|
||||
testTag('hero-di', 'Hero: Windstorm');
|
||||
});
|
||||
|
||||
it('should display component with DI using @Inject example', function () {
|
||||
testTag('hero-di-inject', 'Hero: Windstorm');
|
||||
});
|
||||
|
||||
it('should support optional, attribute, and query injections', function () {
|
||||
let app = element(by.css('hero-di-inject-additional'));
|
||||
let h1 = app.element(by.css('h1'));
|
||||
let okMsg = app.element(by.css('p'));
|
||||
|
||||
expect(h1.getText()).toBe('Tour of Heroes');
|
||||
app.element(by.buttonText('OK')).click();
|
||||
expect(okMsg.getText()).toBe('OK!');
|
||||
});
|
||||
|
||||
it('should support component with inputs and outputs', function () {
|
||||
let app = element(by.css('hero-io'));
|
||||
let confirmComponent = app.element(by.css('app-confirm'));
|
||||
|
||||
confirmComponent.element(by.buttonText('OK')).click();
|
||||
expect(app.element(by.cssContainingText('span', 'OK clicked')).isPresent()).toBe(true);
|
||||
|
||||
confirmComponent.element(by.buttonText('Cancel')).click();
|
||||
expect(app.element(by.cssContainingText('span', 'Cancel clicked')).isPresent()).toBe(true);
|
||||
});
|
||||
|
||||
it('should support host bindings and host listeners', function() {
|
||||
let app = element(by.css('hero-host'));
|
||||
let h1 = app.element(by.css('h1'));
|
||||
|
||||
expect(app.getAttribute('class')).toBe('heading');
|
||||
expect(app.getAttribute('title')).toContain('Tooltip');
|
||||
|
||||
h1.click();
|
||||
expect(h1.getAttribute('class')).toBe('active');
|
||||
|
||||
h1.click();
|
||||
browser.actions().doubleClick(h1.getWebElement()).perform();
|
||||
expect(h1.getAttribute('class')).toBe('active');
|
||||
});
|
||||
|
||||
it('should support content and view queries', function() {
|
||||
let app = element(by.css('hero-queries'));
|
||||
let windstorm = app.element(by.css('view-child:first-child'));
|
||||
|
||||
app.element(by.css('button')).click();
|
||||
expect(windstorm.element(by.css('h2')).getAttribute('class')).toBe('active');
|
||||
expect(windstorm.element(by.css('content-child')).getText()).toBe('Active');
|
||||
});
|
||||
|
||||
function testTag(selector: string, expectedText: string) {
|
||||
let component = element(by.css(selector));
|
||||
expect(component.getText()).toBe(expectedText);
|
||||
}
|
||||
|
||||
});
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"build": "build:babel"
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"description": "TypeScript to JavaScript",
|
||||
"basePath": "src/",
|
||||
"files":[
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js"
|
||||
],
|
||||
"tags":["cookbook"]
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"presets": [
|
||||
"es2015",
|
||||
"angular2"
|
||||
]
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
templateUrl: './app.component.html',
|
||||
styles: [
|
||||
// See hero-di-inject-additional.component
|
||||
'hero-host, hero-host-meta { border: 1px dashed black; display: block; padding: 4px;}',
|
||||
'.heading {font-style: italic}'
|
||||
]
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'ES6 JavaScript with Decorators';
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
<a id="toc"></a>
|
||||
<h1>{{title}}</h1>
|
||||
<a href="#class-metadata">Classes and Class Metadata</a><br>
|
||||
<a href="#io-metadata">Input and Output Decorators</a><br>
|
||||
<a href="#dependency-injection">Dependency Injection</a><br>
|
||||
<a href="#host-metadata">Host Metadata</a><br>
|
||||
<a href="#view-child-metadata">View and Child Metadata</a><br>
|
||||
|
||||
<hr>
|
||||
<h4 id="class-metadata">Classes and Class Metadata</h4>
|
||||
<hero-view></hero-view>
|
||||
<hero-lifecycle></hero-lifecycle>
|
||||
|
||||
<hr>
|
||||
<h4 id="io-metadata">Input and Output Metadata</h4>
|
||||
<hero-io></hero-io>
|
||||
|
||||
<hr>
|
||||
<h4 id="dependency-injection">Dependency Injection</h4>
|
||||
<hero-di></hero-di>
|
||||
<hero-di-inject></hero-di-inject>
|
||||
<hero-di-inject-additional></hero-di-inject-additional>
|
||||
|
||||
<hr>
|
||||
<h4 id="host-metadata">Host Metadata</h4>
|
||||
<hero-host></hero-host>
|
||||
<hero-host-meta></hero-host-meta>
|
||||
|
||||
<hr>
|
||||
<h4 id="view-child-metadata">View and Child Metadata</h4>
|
||||
<hero-queries></hero-queries>
|
|
@ -1,55 +0,0 @@
|
|||
import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { ConfirmComponent } from './confirm.component';
|
||||
// #docregion appimport
|
||||
import { HeroComponent } from './hero.component';
|
||||
// #enddocregion appimport
|
||||
import { HeroComponent as HeroDIComponent } from './hero-di.component';
|
||||
import { HeroComponent as HeroDIInjectComponent } from './hero-di-inject.component';
|
||||
import { HeroComponent as HeroDIInjectAdditionalComponent } from './hero-di-inject-additional.component';
|
||||
import { HeroHostComponent } from './hero-host.component';
|
||||
import { HeroHostMetaComponent } from './hero-host-meta.component';
|
||||
import { HeroIOComponent } from './hero-io.component';
|
||||
import { HeroComponent as HeroLifecycleComponent } from './hero-lifecycle.component';
|
||||
import { HeroQueriesComponent, ViewChildComponent, ContentChildComponent } from './hero-queries.component';
|
||||
import { HeroTitleComponent } from './hero-title.component';
|
||||
|
||||
import { DataService } from './data.service';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule
|
||||
],
|
||||
declarations: [
|
||||
AppComponent,
|
||||
ConfirmComponent,
|
||||
HeroComponent,
|
||||
HeroDIComponent,
|
||||
HeroDIInjectComponent,
|
||||
HeroDIInjectAdditionalComponent,
|
||||
HeroHostComponent, HeroHostMetaComponent,
|
||||
HeroIOComponent,
|
||||
HeroLifecycleComponent,
|
||||
HeroQueriesComponent, ViewChildComponent, ContentChildComponent,
|
||||
HeroTitleComponent
|
||||
],
|
||||
providers: [
|
||||
DataService,
|
||||
{ provide: 'heroName', useValue: 'Windstorm' }
|
||||
],
|
||||
bootstrap: [ AppComponent ],
|
||||
|
||||
// schemas: [ NO_ERRORS_SCHEMA ] // helpful for debugging
|
||||
})
|
||||
export class AppModule { }
|
||||
|
||||
/* tslint:disable no-unused-variable */
|
||||
// #docregion ng2import
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import {
|
||||
LocationStrategy,
|
||||
HashLocationStrategy
|
||||
} from '@angular/common';
|
||||
// #enddocregion ng2import
|
|
@ -1,21 +0,0 @@
|
|||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
|
||||
// #docregion
|
||||
@Component({
|
||||
selector: 'app-confirm',
|
||||
templateUrl: './confirm.component.html'
|
||||
})
|
||||
export class ConfirmComponent {
|
||||
@Input() okMsg = '';
|
||||
@Input('cancelMsg') notOkMsg = '';
|
||||
@Output() ok = new EventEmitter();
|
||||
@Output('cancel') notOk = new EventEmitter();
|
||||
|
||||
onOkClick() {
|
||||
this.ok.emit(true);
|
||||
}
|
||||
onNotOkClick() {
|
||||
this.notOk.emit(true);
|
||||
}
|
||||
}
|
||||
// #enddocregion
|
|
@ -1,6 +0,0 @@
|
|||
<button (click)="onOkClick()">
|
||||
{{okMsg}}
|
||||
</button>
|
||||
<button (click)="onNotOkClick()">
|
||||
{{notOkMsg}}
|
||||
</button>
|
|
@ -1,10 +0,0 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable()
|
||||
export class DataService {
|
||||
constructor() { }
|
||||
|
||||
getHeroName() {
|
||||
return 'Windstorm';
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-di-inject-additional',
|
||||
template: `<hero-title title="Tour of Heroes"></hero-title>`
|
||||
})
|
||||
export class HeroComponent { }
|
|
@ -1,13 +0,0 @@
|
|||
import { Component, Inject } from '@angular/core';
|
||||
|
||||
// #docregion
|
||||
@Component({
|
||||
selector: 'hero-di-inject',
|
||||
template: `<h1>Hero: {{name}}</h1>`
|
||||
})
|
||||
export class HeroComponent {
|
||||
constructor(@Inject('heroName') name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
// #enddocregion
|
|
@ -1,15 +0,0 @@
|
|||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
import { DataService } from './data.service';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-di',
|
||||
template: `<h1>Hero: {{name}}</h1>`
|
||||
})
|
||||
export class HeroComponent {
|
||||
name = '';
|
||||
constructor(dataService: DataService) {
|
||||
this.name = dataService.getHeroName();
|
||||
}
|
||||
}
|
||||
// #enddocregion
|
|
@ -1,44 +0,0 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
// #docregion
|
||||
@Component({
|
||||
selector: 'hero-host-meta',
|
||||
template: `
|
||||
<h1 [class.active]="active">Hero Host in Metadata</h1>
|
||||
<div>Heading clicks: {{clicks}}</div>
|
||||
`,
|
||||
host: {
|
||||
// HostBindings to the <hero-host-meta> element
|
||||
'[title]': 'title',
|
||||
'[class.heading]': 'headingClass',
|
||||
|
||||
// HostListeners on the entire <hero-host-meta> element
|
||||
'(click)': 'clicked()',
|
||||
'(mouseenter)': 'enter($event)',
|
||||
'(mouseleave)': 'leave($event)'
|
||||
},
|
||||
// Styles within (but excluding) the <hero-host-meta> element
|
||||
styles: ['.active {background-color: coral;}']
|
||||
})
|
||||
export class HeroHostMetaComponent {
|
||||
title = 'Hero Host in Metadata Tooltip';
|
||||
headingClass = true;
|
||||
|
||||
active = false;
|
||||
clicks = 0;
|
||||
|
||||
clicked() {
|
||||
this.clicks += 1;
|
||||
}
|
||||
|
||||
enter(event: Event) {
|
||||
this.active = true;
|
||||
this.headingClass = false;
|
||||
}
|
||||
|
||||
leave(event: Event) {
|
||||
this.active = false;
|
||||
this.headingClass = true;
|
||||
}
|
||||
}
|
||||
// #enddocregion
|
|
@ -1,39 +0,0 @@
|
|||
import { Component, HostBinding, HostListener } from '@angular/core';
|
||||
|
||||
// #docregion
|
||||
@Component({
|
||||
selector: 'hero-host',
|
||||
template: `
|
||||
<h1 [class.active]="active">Hero Host in Decorators</h1>
|
||||
<div>Heading clicks: {{clicks}}</div>
|
||||
`,
|
||||
// Styles within (but excluding) the <hero-host> element
|
||||
styles: ['.active {background-color: yellow;}']
|
||||
})
|
||||
export class HeroHostComponent {
|
||||
// HostBindings to the <hero-host> element
|
||||
@HostBinding() title = 'Hero Host in Decorators Tooltip';
|
||||
@HostBinding('class.heading') headingClass = true;
|
||||
|
||||
active = false;
|
||||
clicks = 0;
|
||||
|
||||
// HostListeners on the entire <hero-host> element
|
||||
@HostListener('click')
|
||||
clicked() {
|
||||
this.clicks += 1;
|
||||
}
|
||||
|
||||
@HostListener('mouseenter', ['$event'])
|
||||
enter(event: Event) {
|
||||
this.active = true;
|
||||
this.headingClass = false;
|
||||
}
|
||||
|
||||
@HostListener('mouseleave', ['$event'])
|
||||
leave(event: Event) {
|
||||
this.active = false;
|
||||
this.headingClass = true;
|
||||
}
|
||||
}
|
||||
// #enddocregion
|
|
@ -1,26 +0,0 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-io',
|
||||
template: `
|
||||
<app-confirm [okMsg]="'OK'"
|
||||
[cancelMsg]="'Cancel'"
|
||||
(ok)="onOk()"
|
||||
(cancel)="onCancel()">
|
||||
</app-confirm>
|
||||
<span *ngIf="okClicked">OK clicked</span>
|
||||
<span *ngIf="cancelClicked">Cancel clicked</span>
|
||||
`
|
||||
})
|
||||
export class HeroIOComponent {
|
||||
okClicked = false;
|
||||
cancelClicked = false;
|
||||
|
||||
onOk() {
|
||||
this.okClicked = true;
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
this.cancelClicked = true;
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-lifecycle',
|
||||
template: `<h1>Hero: {{name}}</h1>`
|
||||
})
|
||||
export class HeroComponent {
|
||||
name = '';
|
||||
ngOnInit() {
|
||||
// todo: fetch from server async
|
||||
setTimeout(() => this.name = 'Windstorm', 0);
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
import {
|
||||
Component,
|
||||
ContentChild,
|
||||
Input,
|
||||
QueryList,
|
||||
ViewChildren
|
||||
} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'content-child',
|
||||
template: `
|
||||
<span class="content-child" *ngIf="active">
|
||||
Active
|
||||
</span>`
|
||||
})
|
||||
export class ContentChildComponent {
|
||||
active = false;
|
||||
|
||||
activate() {
|
||||
this.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
// #docregion content
|
||||
@Component({
|
||||
selector: 'view-child',
|
||||
template: `
|
||||
<h2 [class.active]=active>
|
||||
{{hero.name}}
|
||||
<ng-content></ng-content>
|
||||
</h2>`,
|
||||
styles: ['.active {font-weight: bold; background-color: skyblue;}']
|
||||
})
|
||||
export class ViewChildComponent {
|
||||
@Input() hero;
|
||||
active = false;
|
||||
|
||||
@ContentChild(ContentChildComponent) content;
|
||||
|
||||
activate() {
|
||||
this.active = !this.active;
|
||||
this.content.activate();
|
||||
}
|
||||
}
|
||||
// #enddocregion content
|
||||
|
||||
////////////////////
|
||||
|
||||
// #docregion view
|
||||
@Component({
|
||||
selector: 'hero-queries',
|
||||
template: `
|
||||
<view-child *ngFor="let hero of heroData" [hero]="hero">
|
||||
<content-child></content-child>
|
||||
</view-child>
|
||||
<button (click)="activate()">{{buttonLabel}} All</button>
|
||||
`
|
||||
})
|
||||
export class HeroQueriesComponent {
|
||||
active = false;
|
||||
heroData = [
|
||||
{id: 1, name: 'Windstorm'},
|
||||
{id: 2, name: 'LaughingGas'}
|
||||
];
|
||||
|
||||
@ViewChildren(ViewChildComponent) views;
|
||||
|
||||
activate() {
|
||||
this.active = !this.active;
|
||||
this.views.forEach(
|
||||
view => view.activate()
|
||||
);
|
||||
}
|
||||
|
||||
get buttonLabel() {
|
||||
return this.active ? 'Deactivate' : 'Activate';
|
||||
}
|
||||
}
|
||||
// #enddocregion view
|
|
@ -1,25 +0,0 @@
|
|||
import { Attribute, Component, Inject, Optional } from '@angular/core';
|
||||
|
||||
// #docregion
|
||||
// #docregion templateUrl
|
||||
@Component({
|
||||
selector: 'hero-title',
|
||||
templateUrl: './hero-title.component.html'
|
||||
})
|
||||
// #enddocregion templateUrl
|
||||
export class HeroTitleComponent {
|
||||
msg = '';
|
||||
constructor(
|
||||
@Inject('titlePrefix') @Optional() titlePrefix,
|
||||
@Attribute('title') title
|
||||
) {
|
||||
this.titlePrefix = titlePrefix;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
ok() {
|
||||
this.msg = 'OK!';
|
||||
}
|
||||
}
|
||||
// #enddocregion
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<!-- #docregion -->
|
||||
<h1>{{titlePrefix}} {{title}}</h1>
|
||||
<button (click)="ok()">OK</button>
|
||||
<p>{{ msg }}</p>
|
|
@ -1,15 +0,0 @@
|
|||
// #docregion
|
||||
// #docregion metadata
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-view',
|
||||
template: '<h1>{{title}}: {{getName()}}</h1>'
|
||||
})
|
||||
// #docregion appexport, class
|
||||
export class HeroComponent {
|
||||
title = 'Hero Detail';
|
||||
getName() {return 'Windstorm'; }
|
||||
}
|
||||
// #enddocregion appexport, class
|
||||
// #enddocregion metadata
|
|
@ -1,26 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<base href="/">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<title>TypeScript to JavaScript</title>
|
||||
|
||||
<!-- Polyfill(s) for older browsers -->
|
||||
<script src="node_modules/core-js/client/shim.min.js"></script>
|
||||
|
||||
<script src="node_modules/zone.js/dist/zone.js"></script>
|
||||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
|
||||
<script src="systemjs.config.js"></script>
|
||||
<script>
|
||||
System.import('main.js').catch(function(err){ console.error(err); });
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<my-app>Loading...</my-app>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,4 +0,0 @@
|
|||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"build": "build:babel"
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"description": "TypeScript to JavaScript",
|
||||
"basePath": "src/",
|
||||
"files":[
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js"
|
||||
],
|
||||
"tags":["cookbook"]
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"presets": [
|
||||
"es2015"
|
||||
]
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
export class AppComponent {
|
||||
constructor() {
|
||||
this.title = 'Plain ES6 JavaScript';
|
||||
}
|
||||
}
|
||||
|
||||
AppComponent.annotations = [
|
||||
new Component({
|
||||
selector: 'my-app',
|
||||
templateUrl: './app.component.html',
|
||||
styles: [
|
||||
// See hero-di-inject-additional.component
|
||||
'hero-host { border: 1px dashed black; display: block; padding: 4px;}',
|
||||
'.heading {font-style: italic}'
|
||||
]
|
||||
})
|
||||
];
|
|
@ -1,30 +0,0 @@
|
|||
<a id="toc"></a>
|
||||
<h1>{{title}}</h1>
|
||||
<a href="#class-metadata">Classes and Class Metadata</a><br>
|
||||
<a href="#io-metadata">Input and Output Metadata</a><br>
|
||||
<a href="#dependency-injection">Dependency Injection</a><br>
|
||||
<a href="#host-metadata">Host Metadata</a><br>
|
||||
<a href="#view-child-metadata">View and Child Metadata</a><br>
|
||||
|
||||
<hr>
|
||||
<h4 id="class-metadata">Classes and Class Metadata</h4>
|
||||
<hero-view></hero-view>
|
||||
<hero-lifecycle></hero-lifecycle>
|
||||
|
||||
<hr>
|
||||
<h4 id="io-metadata">Input and Output Metadata</h4>
|
||||
<hero-io></hero-io>
|
||||
|
||||
<hr>
|
||||
<h4 id="dependency-injection">Dependency Injection</h4>
|
||||
<hero-di></hero-di>
|
||||
<hero-di-inject></hero-di-inject>
|
||||
<hero-di-inject-additional></hero-di-inject-additional>
|
||||
|
||||
<hr>
|
||||
<h4 id="host-metadata">Host Metadata</h4>
|
||||
<hero-host></hero-host>
|
||||
|
||||
<hr>
|
||||
<h4 id="view-child-metadata">View and Child Metadata</h4>
|
||||
<hero-queries></hero-queries>
|
|
@ -1,56 +0,0 @@
|
|||
import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { ConfirmComponent } from './confirm.component';
|
||||
// #docregion appimport
|
||||
import { HeroComponent } from './hero.component';
|
||||
|
||||
// #enddocregion appimport
|
||||
import { HeroComponent as HeroDIComponent } from './hero-di.component';
|
||||
import { HeroComponent as HeroDIInjectComponent } from './hero-di-inject.component';
|
||||
import { HeroComponent as HeroDIInjectAdditionalComponent } from './hero-di-inject-additional.component';
|
||||
import { HeroHostComponent } from './hero-host.component';
|
||||
import { HeroIOComponent } from './hero-io.component';
|
||||
import { HeroComponent as HeroLifecycleComponent } from './hero-lifecycle.component';
|
||||
import { HeroQueriesComponent, ViewChildComponent, ContentChildComponent } from './hero-queries.component';
|
||||
import { HeroTitleComponent } from './hero-title.component';
|
||||
|
||||
import { DataService } from './data.service';
|
||||
|
||||
export class AppModule { }
|
||||
|
||||
AppModule.annotations = [
|
||||
new NgModule({
|
||||
imports: [ BrowserModule],
|
||||
declarations: [
|
||||
AppComponent,
|
||||
ConfirmComponent,
|
||||
HeroComponent,
|
||||
HeroDIComponent,
|
||||
HeroDIInjectComponent,
|
||||
HeroDIInjectAdditionalComponent,
|
||||
HeroHostComponent,
|
||||
HeroIOComponent,
|
||||
HeroLifecycleComponent,
|
||||
HeroQueriesComponent, ViewChildComponent, ContentChildComponent,
|
||||
HeroTitleComponent
|
||||
],
|
||||
providers: [
|
||||
DataService,
|
||||
{ provide: 'heroName', useValue: 'Windstorm' }
|
||||
],
|
||||
bootstrap: [ AppComponent ],
|
||||
|
||||
// schemas: [ NO_ERRORS_SCHEMA ] // helpful for debugging
|
||||
})
|
||||
]
|
||||
|
||||
/* tslint:disable no-unused-variable */
|
||||
// #docregion ng2import
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import {
|
||||
LocationStrategy,
|
||||
HashLocationStrategy
|
||||
} from '@angular/common';
|
||||
// #enddocregion ng2import
|
|
@ -1,31 +0,0 @@
|
|||
import { Component, EventEmitter } from '@angular/core';
|
||||
|
||||
// #docregion
|
||||
export class ConfirmComponent {
|
||||
constructor(){
|
||||
this.ok = new EventEmitter();
|
||||
this.notOk = new EventEmitter();
|
||||
}
|
||||
onOkClick() {
|
||||
this.ok.emit(true);
|
||||
}
|
||||
onNotOkClick() {
|
||||
this.notOk.emit(true);
|
||||
}
|
||||
}
|
||||
|
||||
ConfirmComponent.annotations = [
|
||||
new Component({
|
||||
selector: 'app-confirm',
|
||||
templateUrl: './confirm.component.html',
|
||||
inputs: [
|
||||
'okMsg',
|
||||
'notOkMsg: cancelMsg'
|
||||
],
|
||||
outputs: [
|
||||
'ok',
|
||||
'notOk: cancel'
|
||||
]
|
||||
})
|
||||
];
|
||||
// #enddocregion
|
|
@ -1,6 +0,0 @@
|
|||
<button (click)="onOkClick()">
|
||||
{{okMsg}}
|
||||
</button>
|
||||
<button (click)="onNotOkClick()">
|
||||
{{notOkMsg}}
|
||||
</button>
|
|
@ -1,13 +0,0 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
|
||||
export class DataService {
|
||||
constructor() {
|
||||
}
|
||||
getHeroName() {
|
||||
return 'Windstorm';
|
||||
}
|
||||
}
|
||||
|
||||
DataService.annotations = [
|
||||
new Injectable()
|
||||
];
|
|
@ -1,10 +0,0 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
export class HeroComponent { }
|
||||
|
||||
HeroComponent.annotations = [
|
||||
new Component({
|
||||
selector: 'hero-di-inject-additional',
|
||||
template: `<hero-title title="Tour of Heroes"></hero-title>`
|
||||
})
|
||||
];
|
|
@ -1,20 +0,0 @@
|
|||
import { Component, Inject } from '@angular/core';
|
||||
|
||||
// #docregion
|
||||
export class HeroComponent {
|
||||
constructor(name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
HeroComponent.annotations = [
|
||||
new Component({
|
||||
selector: 'hero-di-inject',
|
||||
template: `<h1>Hero: {{name}}</h1>`
|
||||
})
|
||||
];
|
||||
|
||||
HeroComponent.parameters = [
|
||||
[new Inject('heroName')]
|
||||
];
|
||||
// #enddocregion
|
|
@ -1,21 +0,0 @@
|
|||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
import { DataService } from './data.service';
|
||||
|
||||
export class HeroComponent {
|
||||
constructor(dataService) {
|
||||
this.name = dataService.getHeroName();
|
||||
}
|
||||
}
|
||||
|
||||
HeroComponent.annotations = [
|
||||
new Component({
|
||||
selector: 'hero-di',
|
||||
template: `<h1>Hero: {{name}}</h1>`
|
||||
})
|
||||
];
|
||||
|
||||
HeroComponent.parameters = [
|
||||
[DataService]
|
||||
];
|
||||
// #enddocregion
|
|
@ -1,50 +0,0 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
// #docregion
|
||||
export class HeroHostComponent {
|
||||
constructor() {
|
||||
this.active = false;
|
||||
this.clicks = 0;
|
||||
this.headingClass = true;
|
||||
this.title = 'Hero Host Tooltip';
|
||||
}
|
||||
|
||||
clicked() {
|
||||
this.clicks += 1;
|
||||
}
|
||||
|
||||
enter(event) {
|
||||
this.active = true;
|
||||
this.headingClass = false;
|
||||
}
|
||||
|
||||
leave(event) {
|
||||
this.active = false;
|
||||
this.headingClass = true;
|
||||
}
|
||||
}
|
||||
|
||||
// #docregion metadata
|
||||
HeroHostComponent.annotations = [
|
||||
new Component({
|
||||
selector: 'hero-host',
|
||||
template: `
|
||||
<h1 [class.active]="active">Hero Host</h1>
|
||||
<div>Heading clicks: {{clicks}}</div>
|
||||
`,
|
||||
host: {
|
||||
// HostBindings to the <hero-host> element
|
||||
'[title]': 'title',
|
||||
'[class.heading]': 'headingClass',
|
||||
'(click)': 'clicked()',
|
||||
|
||||
// HostListeners on the entire <hero-host> element
|
||||
'(mouseenter)': 'enter($event)',
|
||||
'(mouseleave)': 'leave($event)'
|
||||
},
|
||||
// Styles within (but excluding) the <hero-host> element
|
||||
styles: ['.active {background-color: yellow;}']
|
||||
})
|
||||
];
|
||||
// #enddocregion metadata
|
||||
// #enddocregion
|
|
@ -1,31 +0,0 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
export class HeroIOComponent {
|
||||
constructor() {
|
||||
this.okClicked = false;
|
||||
this.cancelClicked = false;
|
||||
}
|
||||
|
||||
onOk() {
|
||||
this.okClicked = true;
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
this.cancelClicked = true;
|
||||
}
|
||||
}
|
||||
|
||||
HeroIOComponent.annotations = [
|
||||
new Component({
|
||||
selector: 'hero-io',
|
||||
template: `
|
||||
<app-confirm [okMsg]="'OK'"
|
||||
[cancelMsg]="'Cancel'"
|
||||
(ok)="onOk()"
|
||||
(cancel)="onCancel()">
|
||||
</app-confirm>
|
||||
<span *ngIf="okClicked">OK clicked</span>
|
||||
<span *ngIf="cancelClicked">Cancel clicked</span>
|
||||
`
|
||||
})
|
||||
];
|
|
@ -1,15 +0,0 @@
|
|||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
export class HeroComponent {
|
||||
ngOnInit() {
|
||||
// todo: fetch from server async
|
||||
setTimeout(() => this.name = 'Windstorm', 0);
|
||||
}
|
||||
}
|
||||
|
||||
HeroComponent.annotations = [
|
||||
new Component({
|
||||
selector: 'hero-lifecycle',
|
||||
template: `<h1>Hero: {{name}}</h1>`
|
||||
})
|
||||
];
|
|
@ -1,97 +0,0 @@
|
|||
import {
|
||||
Component,
|
||||
ContentChild,
|
||||
Input,
|
||||
QueryList,
|
||||
ViewChildren
|
||||
} from '@angular/core';
|
||||
|
||||
export class ContentChildComponent {
|
||||
constructor() {
|
||||
this.active = false;
|
||||
}
|
||||
|
||||
activate() {
|
||||
this.active = !this.active;
|
||||
}
|
||||
}
|
||||
|
||||
ContentChildComponent.annotations = [
|
||||
new Component({
|
||||
selector: 'content-child',
|
||||
template: `
|
||||
<span class="content-child" *ngIf="active">
|
||||
Active
|
||||
</span>`
|
||||
})
|
||||
];
|
||||
|
||||
////////////////////
|
||||
|
||||
// #docregion content
|
||||
export class ViewChildComponent {
|
||||
constructor() {
|
||||
this.active = false;
|
||||
}
|
||||
|
||||
activate() {
|
||||
this.active = !this.active;
|
||||
this.content.activate();
|
||||
}
|
||||
}
|
||||
|
||||
ViewChildComponent.annotations = [
|
||||
new Component({
|
||||
selector: 'view-child',
|
||||
template: `<h2 [class.active]=active>
|
||||
{{hero.name}}
|
||||
<ng-content></ng-content>
|
||||
</h2>`,
|
||||
styles: ['.active {font-weight: bold; background-color: skyblue;}'],
|
||||
inputs: ['hero'],
|
||||
queries: {
|
||||
content: new ContentChild(ContentChildComponent)
|
||||
}
|
||||
})
|
||||
];
|
||||
// #enddocregion content
|
||||
|
||||
////////////////////
|
||||
|
||||
// #docregion view
|
||||
export class HeroQueriesComponent {
|
||||
constructor(){
|
||||
this.active = false;
|
||||
this.heroData = [
|
||||
{id: 1, name: 'Windstorm'},
|
||||
{id: 2, name: 'LaughingGas'}
|
||||
];
|
||||
}
|
||||
|
||||
activate() {
|
||||
this.active = !this.active;
|
||||
this.views.forEach(
|
||||
view => view.activate()
|
||||
);
|
||||
}
|
||||
|
||||
get buttonLabel() {
|
||||
return this.active ? 'Deactivate' : 'Activate';
|
||||
}
|
||||
}
|
||||
|
||||
HeroQueriesComponent.annotations = [
|
||||
new Component({
|
||||
selector: 'hero-queries',
|
||||
template: `
|
||||
<view-child *ngFor="let hero of heroData" [hero]="hero">
|
||||
<content-child></content-child>
|
||||
</view-child>
|
||||
<button (click)="activate()">{{buttonLabel}} All</button>
|
||||
`,
|
||||
queries: {
|
||||
views: new ViewChildren(ViewChildComponent)
|
||||
}
|
||||
})
|
||||
];
|
||||
// #enddocregion view
|
|
@ -1,28 +0,0 @@
|
|||
import { Attribute, Component, Inject, Optional } from '@angular/core';
|
||||
|
||||
// #docregion
|
||||
export class HeroTitleComponent {
|
||||
constructor(titlePrefix, title) {
|
||||
this.titlePrefix = titlePrefix;
|
||||
this.title = title;
|
||||
this.msg = '';
|
||||
}
|
||||
|
||||
ok() {
|
||||
this.msg = 'OK!';
|
||||
}
|
||||
}
|
||||
|
||||
// #docregion templateUrl
|
||||
HeroTitleComponent.annotations = [
|
||||
new Component({
|
||||
selector: 'hero-title',
|
||||
templateUrl: './hero-title.component.html'
|
||||
})
|
||||
];
|
||||
// #enddocregion templateUrl
|
||||
|
||||
HeroTitleComponent.parameters = [
|
||||
[new Optional(), new Inject('titlePrefix')],
|
||||
[new Attribute('title')]
|
||||
];
|
|
@ -1,4 +0,0 @@
|
|||
<!-- #docregion -->
|
||||
<h1>{{titlePrefix}} {{title}}</h1>
|
||||
<button (click)="ok()">OK</button>
|
||||
<p>{{ msg }}</p>
|
|
@ -1,21 +0,0 @@
|
|||
// #docplaster
|
||||
// #docregion
|
||||
// #docregion metadata
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
// #docregion appexport, class
|
||||
export class HeroComponent {
|
||||
constructor() {
|
||||
this.title = 'Hero Detail';
|
||||
}
|
||||
getName() {return 'Windstorm'; }
|
||||
}
|
||||
// #enddocregion appexport, class
|
||||
|
||||
HeroComponent.annotations = [
|
||||
new Component({
|
||||
selector: 'hero-view',
|
||||
template: '<h1>{{title}}: {{getName()}}</h1>'
|
||||
})
|
||||
];
|
||||
// #enddocregion metadata
|
|
@ -1,26 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<base href="/">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<title>TypeScript to JavaScript</title>
|
||||
|
||||
<!-- Polyfill(s) for older browsers -->
|
||||
<script src="node_modules/core-js/client/shim.min.js"></script>
|
||||
|
||||
<script src="node_modules/zone.js/dist/zone.js"></script>
|
||||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
|
||||
<script src="systemjs.config.js"></script>
|
||||
<script>
|
||||
System.import('main.js').catch(function(err){ console.error(err); });
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<my-app>Loading...</my-app>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,4 +0,0 @@
|
|||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"build": "build:babel"
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"description": "TypeScript to JavaScript",
|
||||
"basePath": "src/",
|
||||
"files":[
|
||||
"!**/karma*.*"
|
||||
],
|
||||
"tags":["cookbook"]
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
<a id="toc"></a>
|
||||
<h1>{{title}}</h1>
|
||||
<a href="#class-metadata">Classes and Class Metadata</a><br>
|
||||
<a href="#interfaces">Interfaces</a><br>
|
||||
<a href="#io-metadata">Input and Output Metadata</a><br>
|
||||
<a href="#dependency-injection">Dependency Injection</a><br>
|
||||
<a href="#host-metadata">Host Metadata</a><br>
|
||||
<a href="#view-child-metadata">View and Child Metadata</a><br>
|
||||
|
||||
<hr>
|
||||
<h4 id="class-metadata">Classes and Class Metadata</h4>
|
||||
<hero-view></hero-view>
|
||||
<h4 id="class-metadata-dsl">Classes and Class Metadata (DSL)</h4>
|
||||
<hero-view-dsl></hero-view-dsl>
|
||||
|
||||
<hr>
|
||||
<h4 id="interfaces">Interfaces</h4>
|
||||
<hero-lifecycle></hero-lifecycle>
|
||||
<h4 id="interfaces-dsl">Interfaces (DSL)</h4>
|
||||
<hero-lifecycle-dsl></hero-lifecycle-dsl>
|
||||
|
||||
<hr>
|
||||
<h4 id="io-metadata">Input and Output Metadata</h4>
|
||||
<hero-io></hero-io>
|
||||
<h4 id="io-metadata-dsl">Input and Output Metadata (DSL)</h4>
|
||||
<hero-io-dsl></hero-io-dsl>
|
||||
|
||||
<hr>
|
||||
<h4 id="dependency-injection">Dependency Injection</h4>
|
||||
<hero-di></hero-di>
|
||||
<hero-di-inject></hero-di-inject>
|
||||
<hero-di-inject-additional></hero-di-inject-additional>
|
||||
|
||||
<h4 id="dependency-injection-dsl">Dependency Injection (DSL)</h4>
|
||||
<hero-di-dsl></hero-di-dsl>
|
||||
<hero-di-inject-dsl></hero-di-inject-dsl>
|
||||
<hero-di-inject-additional-dsl></hero-di-inject-additional-dsl>
|
||||
|
||||
<hr>
|
||||
<h4 id="host-metadata">Host Metadata</h4>
|
||||
<hero-host></hero-host>
|
||||
<h4 id="host-metadata-dsl">Host Metadata (DSL)</h4>
|
||||
<hero-host-dsl></hero-host-dsl>
|
||||
|
||||
<hr>
|
||||
<h4 id="view-child-metadata">View and Child Metadata (DSL)</h4>
|
||||
<hero-queries></hero-queries>
|
|
@ -1,20 +0,0 @@
|
|||
(function(app) {
|
||||
|
||||
app.AppComponent = AppComponent;
|
||||
function AppComponent() {
|
||||
this.title = 'ES5 JavaScript';
|
||||
}
|
||||
|
||||
AppComponent.annotations = [
|
||||
new ng.core.Component({
|
||||
selector: 'my-app',
|
||||
templateUrl: 'app/app.component.html',
|
||||
styles: [
|
||||
// See hero-di-inject-additional.component
|
||||
'hero-host, hero-host-dsl { border: 1px dashed black; display: block; padding: 4px;}',
|
||||
'.heading {font-style: italic}'
|
||||
]
|
||||
})
|
||||
];
|
||||
|
||||
})(window.app = window.app || {});
|
|
@ -1,46 +0,0 @@
|
|||
(function(app) {
|
||||
|
||||
app.AppModule = AppModule;
|
||||
function AppModule() { }
|
||||
|
||||
AppModule.annotations = [
|
||||
new ng.core.NgModule({
|
||||
imports: [ ng.platformBrowser.BrowserModule ],
|
||||
declarations: [
|
||||
app.AppComponent,
|
||||
app.ConfirmComponent, app.ConfirmDslComponent,
|
||||
app.HeroComponent, app.HeroDslComponent,
|
||||
app.HeroDIComponent, app.HeroDIDslComponent,
|
||||
app.HeroDIInjectComponent, app.HeroDIInjectDslComponent,
|
||||
app.HeroDIInjectAdditionalComponent, app.HeroDIInjectAdditionalDslComponent,
|
||||
app.HeroHostComponent, app.HeroHostDslComponent,
|
||||
app.HeroIOComponent, app.HeroIODslComponent,
|
||||
app.HeroLifecycleComponent, app.HeroLifecycleDslComponent,
|
||||
app.heroQueries.HeroQueriesComponent, app.heroQueries.ViewChildComponent, app.heroQueries.ContentChildComponent,
|
||||
app.HeroTitleComponent, app.HeroTitleDslComponent
|
||||
],
|
||||
providers: [
|
||||
app.DataService,
|
||||
{ provide: 'heroName', useValue: 'Windstorm' }
|
||||
],
|
||||
bootstrap: [ app.AppComponent ],
|
||||
|
||||
// schemas: [ ng.core.NO_ERRORS_SCHEMA ] // helpful for debugging!
|
||||
})
|
||||
]
|
||||
|
||||
})(window.app = window.app || {});
|
||||
|
||||
|
||||
///// For documentation only /////
|
||||
(function () {
|
||||
// #docregion appimport
|
||||
var HeroComponent = app.HeroComponent;
|
||||
// #enddocregion appimport
|
||||
|
||||
// #docregion ng2import
|
||||
var platformBrowserDynamic = ng.platformBrowserDynamic.platformBrowserDynamic;
|
||||
var LocationStrategy = ng.common.LocationStrategy;
|
||||
var HashLocationStrategy = ng.common.HashLocationStrategy;
|
||||
// #enddocregion ng2import
|
||||
})
|
|
@ -1,6 +0,0 @@
|
|||
<button (click)="onOkClick()">
|
||||
{{okMsg}}
|
||||
</button>
|
||||
<button (click)="onNotOkClick()">
|
||||
{{notOkMsg}}
|
||||
</button>
|
|
@ -1,75 +0,0 @@
|
|||
(function(app) {
|
||||
|
||||
// #docregion
|
||||
app.ConfirmComponent = ConfirmComponent;
|
||||
|
||||
ConfirmComponent.annotations = [
|
||||
new ng.core.Component({
|
||||
selector: 'app-confirm',
|
||||
templateUrl: 'app/confirm.component.html',
|
||||
inputs: [
|
||||
'okMsg',
|
||||
'notOkMsg: cancelMsg'
|
||||
],
|
||||
outputs: [
|
||||
'ok',
|
||||
'notOk: cancel'
|
||||
]
|
||||
})
|
||||
];
|
||||
|
||||
function ConfirmComponent() {
|
||||
this.ok = new ng.core.EventEmitter();
|
||||
this.notOk = new ng.core.EventEmitter();
|
||||
}
|
||||
|
||||
ConfirmComponent.prototype.onOkClick = function() {
|
||||
this.ok.emit(true);
|
||||
}
|
||||
|
||||
ConfirmComponent.prototype.onNotOkClick = function() {
|
||||
this.notOk.emit(true);
|
||||
}
|
||||
// #enddocregion
|
||||
|
||||
})(window.app = window.app || {});
|
||||
|
||||
/////// DSL version ////////
|
||||
|
||||
(function(app) {
|
||||
|
||||
var old = app.ConfirmComponent;
|
||||
|
||||
// #docregion dsl
|
||||
app.ConfirmComponent = ng.core.Component({
|
||||
selector: 'app-confirm-dsl',
|
||||
templateUrl: 'app/confirm.component.html',
|
||||
inputs: [
|
||||
'okMsg',
|
||||
'notOkMsg: cancelMsg'
|
||||
],
|
||||
outputs: [
|
||||
'ok',
|
||||
'notOk: cancel'
|
||||
]
|
||||
})
|
||||
.Class({
|
||||
constructor: function ConfirmComponent() {
|
||||
this.ok = new ng.core.EventEmitter();
|
||||
this.notOk = new ng.core.EventEmitter();
|
||||
},
|
||||
|
||||
onOkClick: function() {
|
||||
this.ok.emit(true);
|
||||
},
|
||||
|
||||
onNotOkClick: function() {
|
||||
this.notOk.emit(true);
|
||||
}
|
||||
});
|
||||
// #enddocregion dsl
|
||||
|
||||
app.ConfirmDslComponent = app.ConfirmComponent;
|
||||
app.ConfirmComponent = old;
|
||||
|
||||
})(window.app = window.app || {});
|
|
@ -1,10 +0,0 @@
|
|||
(function(app) {
|
||||
|
||||
app.DataService = DataService;
|
||||
function DataService() { }
|
||||
|
||||
DataService.prototype.getHeroName = function() {
|
||||
return 'Windstorm';
|
||||
};
|
||||
|
||||
})(window.app = window.app || {});
|
|
@ -1,36 +0,0 @@
|
|||
(function(app) {
|
||||
|
||||
var old = app.HeroComponent;
|
||||
|
||||
app.HeroComponent = HeroComponent;
|
||||
|
||||
HeroComponent.annotations = [
|
||||
new ng.core.Component({
|
||||
selector: 'hero-di-inject-additional',
|
||||
template: '<hero-title title="Tour of Heroes"></hero-title>'
|
||||
})
|
||||
];
|
||||
|
||||
function HeroComponent() {}
|
||||
|
||||
app.HeroDIInjectAdditionalComponent = app.HeroComponent;
|
||||
app.HeroComponent = old;
|
||||
|
||||
})(window.app = window.app || {});
|
||||
|
||||
////// DSL Version /////////
|
||||
(function(app) {
|
||||
|
||||
var old = app.HeroComponent;
|
||||
|
||||
app.HeroComponent = ng.core.Component({
|
||||
selector: 'hero-di-inject-additional-dsl',
|
||||
template: '<hero-title-dsl title="Tour of Heroes"></hero-title-dsl>'
|
||||
}).Class({
|
||||
constructor: function HeroComponent() { }
|
||||
});
|
||||
|
||||
app.HeroDIInjectAdditionalDslComponent = app.HeroComponent;
|
||||
app.HeroComponent = old;
|
||||
|
||||
})(window.app = window.app || {});
|
|
@ -1,51 +0,0 @@
|
|||
(function(app) {
|
||||
|
||||
var old = app.HeroComponent;
|
||||
|
||||
// #docregion
|
||||
app.HeroComponent = HeroComponent;
|
||||
|
||||
HeroComponent.annotations = [
|
||||
new ng.core.Component({
|
||||
selector: 'hero-di-inject',
|
||||
template: '<h1>Hero: {{name}}</h1>'
|
||||
})
|
||||
];
|
||||
|
||||
HeroComponent.parameters = [ 'heroName' ];
|
||||
|
||||
function HeroComponent(name) {
|
||||
this.name = name;
|
||||
}
|
||||
// #enddocregion
|
||||
|
||||
app.HeroDIInjectComponent = app.HeroComponent;
|
||||
app.HeroComponent = old;
|
||||
|
||||
})(window.app = window.app || {});
|
||||
|
||||
/////// DSL version ////////
|
||||
|
||||
(function(app) {
|
||||
|
||||
var old = app.HeroComponent;
|
||||
|
||||
// #docregion dsl
|
||||
app.HeroComponent = ng.core.Component({
|
||||
selector: 'hero-di-inject-dsl',
|
||||
template: '<h1>Hero: {{name}}</h1>'
|
||||
})
|
||||
.Class({
|
||||
constructor: [
|
||||
new ng.core.Inject('heroName'),
|
||||
function HeroComponent(name) {
|
||||
this.name = name;
|
||||
}
|
||||
]
|
||||
});
|
||||
// #enddocregion dsl
|
||||
|
||||
app.HeroDIInjectDslComponent = app.HeroComponent;
|
||||
app.HeroComponent = old;
|
||||
|
||||
})(window.app = window.app || {});
|
|
@ -1,51 +0,0 @@
|
|||
(function(app) {
|
||||
|
||||
var old = app.HeroComponent;
|
||||
|
||||
// #docregion
|
||||
app.HeroComponent = HeroComponent;
|
||||
|
||||
HeroComponent.annotations = [
|
||||
new ng.core.Component({
|
||||
selector: 'hero-di',
|
||||
template: '<h1>Hero: {{name}}</h1>'
|
||||
})
|
||||
];
|
||||
|
||||
HeroComponent.parameters = [ app.DataService ];
|
||||
|
||||
function HeroComponent(dataService) {
|
||||
this.name = dataService.getHeroName();
|
||||
}
|
||||
// #enddocregion
|
||||
|
||||
app.HeroDIComponent = app.HeroComponent;
|
||||
app.HeroComponent = old;
|
||||
|
||||
})(window.app = window.app || {});
|
||||
|
||||
////// DSL Version /////
|
||||
|
||||
(function(app) {
|
||||
|
||||
var old = app.HeroComponent;
|
||||
|
||||
// #docregion dsl
|
||||
app.HeroComponent = ng.core.Component({
|
||||
selector: 'hero-di-dsl',
|
||||
template: '<h1>Hero: {{name}}</h1>'
|
||||
})
|
||||
.Class({
|
||||
constructor: [
|
||||
app.DataService,
|
||||
function HeroComponent(service) {
|
||||
this.name = service.getHeroName();
|
||||
}
|
||||
]
|
||||
});
|
||||
// #enddocregion dsl
|
||||
|
||||
app.HeroDIDslComponent = app.HeroComponent;
|
||||
app.HeroComponent = old;
|
||||
|
||||
})(window.app = window.app || {});
|
|
@ -1,107 +0,0 @@
|
|||
(function(app) {
|
||||
|
||||
var old = app.HeroComponent
|
||||
|
||||
// #docregion
|
||||
app.HeroComponent = HeroComponent;
|
||||
|
||||
HeroComponent.annotations = [
|
||||
new ng.core.Component({
|
||||
selector: 'hero-host',
|
||||
template:
|
||||
'<h1 [class.active]="active">Hero Host</h1>' +
|
||||
'<div>Heading clicks: {{clicks}}</div>',
|
||||
host: {
|
||||
// HostBindings to the <hero-host> element
|
||||
'[title]': 'title',
|
||||
'[class.heading]': 'headingClass',
|
||||
'(click)': 'clicked()',
|
||||
|
||||
// HostListeners on the entire <hero-host> element
|
||||
'(mouseenter)': 'enter($event)',
|
||||
'(mouseleave)': 'leave($event)'
|
||||
},
|
||||
// Styles within (but excluding) the <hero-host> element
|
||||
styles: ['.active {background-color: yellow;}']
|
||||
})
|
||||
];
|
||||
|
||||
function HeroComponent() {
|
||||
this.clicks = 0;
|
||||
this.headingClass = true;
|
||||
this.title = 'Hero Host Tooltip content';
|
||||
}
|
||||
|
||||
HeroComponent.prototype.clicked = function() {
|
||||
this.clicks += 1;
|
||||
}
|
||||
|
||||
HeroComponent.prototype.enter = function(event) {
|
||||
this.active = true;
|
||||
this.headingClass = false;
|
||||
}
|
||||
|
||||
HeroComponent.prototype.leave = function(event) {
|
||||
this.active = false;
|
||||
this.headingClass = true;
|
||||
}
|
||||
// #enddocregion
|
||||
|
||||
app.HeroHostComponent = app.HeroComponent;
|
||||
app.HeroComponent = old;
|
||||
|
||||
})(window.app = window.app || {});
|
||||
|
||||
//// DSL Version ////
|
||||
|
||||
(function(app) {
|
||||
|
||||
var old = app.HeroComponent;
|
||||
|
||||
// #docregion dsl
|
||||
app.HeroComponent = ng.core.Component({
|
||||
selector: 'hero-host-dsl',
|
||||
template: `
|
||||
<h1 [class.active]="active">Hero Host (DSL)</h1>
|
||||
<div>Heading clicks: {{clicks}}</div>
|
||||
`,
|
||||
host: {
|
||||
// HostBindings to the <hero-host-dsl> element
|
||||
'[title]': 'title',
|
||||
'[class.heading]': 'headingClass',
|
||||
'(click)': 'clicked()',
|
||||
|
||||
// HostListeners on the entire <hero-host-dsl> element
|
||||
'(mouseenter)': 'enter($event)',
|
||||
'(mouseleave)': 'leave($event)'
|
||||
},
|
||||
// Styles within (but excluding) the <hero-host-dsl> element
|
||||
styles: ['.active {background-color: coral;}']
|
||||
})
|
||||
.Class({
|
||||
constructor: function HeroComponent() {
|
||||
this.clicks = 0;
|
||||
this.headingClass = true;
|
||||
this.title = 'Hero Host Tooltip DSL content';
|
||||
},
|
||||
|
||||
clicked() {
|
||||
this.clicks += 1;
|
||||
},
|
||||
|
||||
enter(event) {
|
||||
this.active = true;
|
||||
this.headingClass = false;
|
||||
},
|
||||
|
||||
leave(event) {
|
||||
this.active = false;
|
||||
this.headingClass = true;
|
||||
}
|
||||
});
|
||||
// #enddocregion dsl
|
||||
|
||||
app.HeroHostDslComponent = app.HeroComponent;
|
||||
app.HeroComponent = old;
|
||||
|
||||
})(window.app = window.app || {});
|
|
@ -1,7 +0,0 @@
|
|||
<app-confirm-dsl [okMsg]="'OK'"
|
||||
[cancelMsg]="'Cancel'"
|
||||
(ok)="onOk()"
|
||||
(cancel)="onCancel()">
|
||||
</app-confirm-dsl>
|
||||
<span *ngIf="okClicked">OK clicked</span>
|
||||
<span *ngIf="cancelClicked">Cancel clicked</span>
|
|
@ -1,7 +0,0 @@
|
|||
<app-confirm [okMsg]="'OK'"
|
||||
[cancelMsg]="'Cancel'"
|
||||
(ok)="onOk()"
|
||||
(cancel)="onCancel()">
|
||||
</app-confirm>
|
||||
<span *ngIf="okClicked">OK clicked</span>
|
||||
<span *ngIf="cancelClicked">Cancel clicked</span>
|
|
@ -1,52 +0,0 @@
|
|||
(function(app) {
|
||||
|
||||
var old = app.HeroComponent
|
||||
|
||||
app.HeroComponent = HeroComponent;
|
||||
|
||||
HeroComponent.annotations = [
|
||||
new ng.core.Component({
|
||||
selector: 'hero-io',
|
||||
templateUrl: 'app/hero-io.component.html'
|
||||
})
|
||||
];
|
||||
|
||||
function HeroComponent() { }
|
||||
|
||||
HeroComponent.prototype.onOk = function() {
|
||||
this.okClicked = true;
|
||||
}
|
||||
|
||||
HeroComponent.prototype.onCancel = function() {
|
||||
this.cancelClicked = true;
|
||||
}
|
||||
|
||||
app.HeroIOComponent = app.HeroComponent;
|
||||
app.HeroComponent = old;
|
||||
|
||||
})(window.app = window.app || {});
|
||||
|
||||
///// DSL Version ////
|
||||
|
||||
(function(app) {
|
||||
|
||||
var old = app.HeroComponent
|
||||
|
||||
app.HeroComponent = ng.core.Component({
|
||||
selector: 'hero-io-dsl',
|
||||
templateUrl: 'app/hero-io-dsl.component.html'
|
||||
})
|
||||
.Class({
|
||||
constructor: function HeroComponent() { },
|
||||
onOk: function() {
|
||||
this.okClicked = true;
|
||||
},
|
||||
onCancel: function() {
|
||||
this.cancelClicked = true;
|
||||
}
|
||||
});
|
||||
|
||||
app.HeroIODslComponent = app.HeroComponent;
|
||||
app.HeroComponent = old;
|
||||
|
||||
})(window.app = window.app || {});
|
|
@ -1,52 +0,0 @@
|
|||
// #docplaster
|
||||
(function(app) {
|
||||
|
||||
var old = app.HeroComponent;
|
||||
|
||||
// #docregion
|
||||
app.HeroComponent = HeroComponent;
|
||||
|
||||
HeroComponent.annotations = [
|
||||
new ng.core.Component({
|
||||
selector: 'hero-lifecycle',
|
||||
template: '<h1>Hero: {{name}}</h1>'
|
||||
})
|
||||
];
|
||||
|
||||
function HeroComponent() { }
|
||||
|
||||
HeroComponent.prototype.ngOnInit = function() {
|
||||
// todo: fetch from server async
|
||||
setTimeout(() => this.name = 'Windstorm', 0);
|
||||
};
|
||||
// #enddocregion
|
||||
|
||||
app.HeroLifecycleComponent = app.HeroComponent;
|
||||
app.HeroComponent = old;
|
||||
|
||||
})(window.app = window.app || {});
|
||||
|
||||
/////// DSL version ////
|
||||
|
||||
(function(app) {
|
||||
|
||||
var old = app.HeroComponent;
|
||||
|
||||
// #docregion dsl
|
||||
app.HeroComponent = ng.core.Component({
|
||||
selector: 'hero-lifecycle-dsl',
|
||||
template: '<h1>Hero: {{name}}</h1>'
|
||||
})
|
||||
.Class({
|
||||
constructor: function HeroComponent() { },
|
||||
ngOnInit: function() {
|
||||
// todo: fetch from server async
|
||||
setTimeout(() => this.name = 'Windstorm', 0);
|
||||
}
|
||||
});
|
||||
// #enddocregion dsl
|
||||
|
||||
app.HeroLifecycleDslComponent = app.HeroComponent;
|
||||
app.HeroComponent = old;
|
||||
|
||||
})(window.app = window.app || {});
|
|
@ -1,92 +0,0 @@
|
|||
(function(app) {
|
||||
|
||||
app.heroQueries = app.heroQueries || {};
|
||||
|
||||
app.heroQueries.ContentChildComponent = ng.core.Component({
|
||||
selector: 'content-child',
|
||||
template:
|
||||
'<span class="content-child" *ngIf="active">' +
|
||||
'Active' +
|
||||
'</span>'
|
||||
}).Class({
|
||||
constructor: function ContentChildComponent() {
|
||||
this.active = false;
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
this.active = !this.active;
|
||||
}
|
||||
});
|
||||
|
||||
////////////////////
|
||||
|
||||
// #docregion content
|
||||
app.heroQueries.ViewChildComponent = ng.core.Component({
|
||||
selector: 'view-child',
|
||||
template:
|
||||
'<h2 [class.active]=active>' +
|
||||
'{{hero.name}} ' +
|
||||
'<ng-content></ng-content>' +
|
||||
'</h2>',
|
||||
styles: ['.active {font-weight: bold; background-color: skyblue;}'],
|
||||
inputs: ['hero'],
|
||||
queries: {
|
||||
content: new ng.core.ContentChild(app.heroQueries.ContentChildComponent)
|
||||
}
|
||||
})
|
||||
.Class({
|
||||
constructor: function HeroQueriesHeroComponent() {
|
||||
this.active = false;
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
this.active = !this.active;
|
||||
this.content.activate();
|
||||
}
|
||||
});
|
||||
// #enddocregion content
|
||||
|
||||
////////////////////
|
||||
|
||||
// #docregion view
|
||||
app.heroQueries.HeroQueriesComponent = ng.core.Component({
|
||||
selector: 'hero-queries',
|
||||
template:
|
||||
'<view-child *ngFor="let hero of heroData" [hero]="hero">' +
|
||||
'<content-child></content-child>' +
|
||||
'</view-child>' +
|
||||
'<button (click)="activate()">{{buttonLabel}} All</button>',
|
||||
queries: {
|
||||
views: new ng.core.ViewChildren(app.heroQueries.ViewChildComponent)
|
||||
}
|
||||
})
|
||||
.Class({
|
||||
constructor: function HeroQueriesComponent() {
|
||||
this.active = false;
|
||||
this.heroData = [
|
||||
{id: 1, name: 'Windstorm'},
|
||||
{id: 2, name: 'LaughingGas'}
|
||||
];
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
this.active = !this.active;
|
||||
this.views.forEach(function(view) {
|
||||
view.activate();
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// #docregion defined-property
|
||||
// add prototype property w/ getter outside the DSL
|
||||
var proto = app.heroQueries.HeroQueriesComponent.prototype;
|
||||
Object.defineProperty(proto, "buttonLabel", {
|
||||
get: function () {
|
||||
return this.active ? 'Deactivate' : 'Activate';
|
||||
},
|
||||
enumerable: true
|
||||
});
|
||||
// #enddocregion defined-property
|
||||
// #enddocregion view
|
||||
|
||||
})(window.app = window.app || {});
|
|
@ -1,4 +0,0 @@
|
|||
<!-- #docregion -->
|
||||
<h1>{{titlePrefix}} {{title}}</h1>
|
||||
<button (click)="ok()">OK</button>
|
||||
<p>{{ msg }}</p>
|
|
@ -1,64 +0,0 @@
|
|||
(function(app) {
|
||||
|
||||
// #docregion
|
||||
app.HeroTitleComponent = HeroTitleComponent;
|
||||
|
||||
// #docregion templateUrl
|
||||
HeroTitleComponent.annotations = [
|
||||
new ng.core.Component({
|
||||
selector: 'hero-title',
|
||||
templateUrl: 'app/hero-title.component.html'
|
||||
})
|
||||
];
|
||||
// #enddocregion templateUrl
|
||||
|
||||
function HeroTitleComponent(titlePrefix, title) {
|
||||
this.titlePrefix = titlePrefix;
|
||||
this.title = title;
|
||||
this.msg = '';
|
||||
}
|
||||
|
||||
HeroTitleComponent.prototype.ok = function() {
|
||||
this.msg = 'OK!';
|
||||
}
|
||||
|
||||
HeroTitleComponent.parameters = [
|
||||
[new ng.core.Optional(), new ng.core.Inject('titlePrefix')],
|
||||
[new ng.core.Attribute('title')]
|
||||
];
|
||||
// #enddocregion
|
||||
|
||||
})(window.app = window.app || {});
|
||||
|
||||
////////// DSL version ////////////
|
||||
|
||||
(function(app) {
|
||||
|
||||
var old = app.HeroTitleComponent;
|
||||
|
||||
// #docregion dsl
|
||||
app.HeroTitleComponent = ng.core.Component({
|
||||
selector: 'hero-title-dsl',
|
||||
templateUrl: 'app/hero-title.component.html'
|
||||
})
|
||||
.Class({
|
||||
constructor: [
|
||||
[ new ng.core.Optional(), new ng.core.Inject('titlePrefix') ],
|
||||
new ng.core.Attribute('title'),
|
||||
function HeroTitleComponent(titlePrefix, title) {
|
||||
this.titlePrefix = titlePrefix;
|
||||
this.title = title;
|
||||
this.msg = '';
|
||||
}
|
||||
],
|
||||
|
||||
ok: function() {
|
||||
this.msg = 'OK!';
|
||||
}
|
||||
});
|
||||
// #enddocregion dsl
|
||||
|
||||
app.HeroTitleDslComponent = app.HeroTitleComponent;
|
||||
app.HeroTitleComponent = old;
|
||||
|
||||
})(window.app = window.app || {});
|
|
@ -1,53 +0,0 @@
|
|||
// #docplaster
|
||||
(function(app) {
|
||||
|
||||
// #docregion
|
||||
// #docregion appexport
|
||||
// #docregion metadata
|
||||
app.HeroComponent = HeroComponent; // "export"
|
||||
|
||||
HeroComponent.annotations = [
|
||||
new ng.core.Component({
|
||||
selector: 'hero-view',
|
||||
template: '<h1>{{title}}: {{getName()}}</h1>'
|
||||
})
|
||||
];
|
||||
|
||||
// #docregion constructorproto
|
||||
function HeroComponent() {
|
||||
this.title = "Hero Detail";
|
||||
}
|
||||
|
||||
HeroComponent.prototype.getName = function() { return 'Windstorm'; };
|
||||
// #enddocregion constructorproto
|
||||
|
||||
// #enddocregion metadata
|
||||
// #enddocregion appexport
|
||||
// #enddocregion
|
||||
|
||||
})(window.app = window.app || {});
|
||||
|
||||
//////////// DSL version ///////////
|
||||
|
||||
(function(app) {
|
||||
|
||||
var old = app.HeroComponent;
|
||||
|
||||
// #docregion dsl
|
||||
app.HeroComponent = ng.core.Component({
|
||||
selector: 'hero-view-dsl',
|
||||
template: '<h1>{{title}}: {{getName()}}</h1>',
|
||||
})
|
||||
.Class({
|
||||
constructor: function HeroComponent() {
|
||||
this.title = "Hero Detail";
|
||||
},
|
||||
|
||||
getName: function() { return 'Windstorm'; }
|
||||
});
|
||||
// #enddocregion dsl
|
||||
|
||||
app.HeroDslComponent = app.HeroComponent;
|
||||
app.HeroComponent = old;
|
||||
|
||||
})(window.app = window.app || {});
|
|
@ -1,43 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<title>TypeScript to JavaScript</title>
|
||||
|
||||
<script src="node_modules/core-js/client/shim.min.js"></script>
|
||||
|
||||
<script src="node_modules/zone.js/dist/zone.js"></script>
|
||||
|
||||
<!-- Angular and RxJS umd scripts -->
|
||||
<script src="node_modules/rxjs/bundles/Rx.js"></script>
|
||||
<script src="node_modules/@angular/core/bundles/core.umd.js"></script>
|
||||
<script src="node_modules/@angular/common/bundles/common.umd.js"></script>
|
||||
<script src="node_modules/@angular/compiler/bundles/compiler.umd.js"></script>
|
||||
<script src="node_modules/@angular/platform-browser/bundles/platform-browser.umd.js"></script>
|
||||
<script src="node_modules/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js"></script>
|
||||
|
||||
<!-- Application scripts -->
|
||||
<script src="app/app.component.js"></script>
|
||||
<script src="app/confirm.component.js"></script>
|
||||
<script src="app/data.service.js"></script>
|
||||
<script src="app/hero.component.js"></script>
|
||||
<script src="app/hero-io.component.js"></script>
|
||||
<script src="app/hero-di.component.js"></script>
|
||||
<script src="app/hero-di-inject.component.js"></script>
|
||||
<script src="app/hero-di-inject-additional.component.js"></script>
|
||||
<script src="app/hero-host.component.js"></script>
|
||||
<script src="app/hero-lifecycle.component.js"></script>
|
||||
<script src="app/hero-queries.component.js"></script>
|
||||
<script src="app/hero-title.component.js"></script>
|
||||
|
||||
<script src="app/app.module.js"></script>
|
||||
<script src="main.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<my-app>Loading...</my-app>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,9 +0,0 @@
|
|||
(function(app) {
|
||||
|
||||
var platformBrowserDynamic = ng.platformBrowserDynamic.platformBrowserDynamic;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
platformBrowserDynamic().bootstrapModule(app.AppModule);
|
||||
});
|
||||
|
||||
})(window.app = window.app || {});
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"description": "TypeScript to JavaScript",
|
||||
"basePath": "src/",
|
||||
"files":[
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js"
|
||||
],
|
||||
"tags":["cookbook"]
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
<a id="toc"></a>
|
||||
<h1>{{title}}</h1>
|
||||
<a href="#class-metadata">Classes and Class Metadata</a><br>
|
||||
<a href="#io-metadata">Input and Output Decorators</a><br>
|
||||
<a href="#dependency-injection">Dependency Injection</a><br>
|
||||
<a href="#host-metadata">Host Metadata</a><br>
|
||||
<a href="#view-child-metadata">View and Child Metadata</a><br>
|
||||
|
||||
<hr>
|
||||
<h4 id="class-metadata">Classes and Class Metadata</h4>
|
||||
<hero-view></hero-view>
|
||||
<hero-lifecycle></hero-lifecycle>
|
||||
|
||||
<hr>
|
||||
<h4 id="io-metadata">Input and Output Metadata</h4>
|
||||
<hero-io></hero-io>
|
||||
|
||||
<hr>
|
||||
<h4 id="dependency-injection">Dependency Injection</h4>
|
||||
<hero-di></hero-di>
|
||||
<hero-di-inject></hero-di-inject>
|
||||
<hero-di-inject-additional></hero-di-inject-additional>
|
||||
|
||||
<hr>
|
||||
<h4 id="host-metadata">Host Metadata</h4>
|
||||
<hero-host></hero-host>
|
||||
<hero-host-meta></hero-host-meta>
|
||||
|
||||
<hr>
|
||||
<h4 id="view-child-metadata">View and Child Metadata</h4>
|
||||
<hero-queries></hero-queries>
|
|
@ -1,14 +0,0 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
templateUrl: './app.component.html',
|
||||
styles: [
|
||||
// See hero-di-inject-additional.component
|
||||
'hero-host, hero-host-meta { border: 1px dashed black; display: block; padding: 4px;}',
|
||||
'.heading {font-style: italic}'
|
||||
]
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'TypeScript';
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
/* tslint:disable-next-line:no-unused-variable */
|
||||
import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { ConfirmComponent } from './confirm.component';
|
||||
// #docregion appimport
|
||||
import { HeroComponent } from './hero.component';
|
||||
// #enddocregion appimport
|
||||
import { HeroComponent as HeroDIComponent } from './hero-di.component';
|
||||
import { HeroComponent as HeroDIInjectComponent } from './hero-di-inject.component';
|
||||
import { HeroComponent as HeroDIInjectAdditionalComponent } from './hero-di-inject-additional.component';
|
||||
import { HeroHostComponent } from './hero-host.component';
|
||||
import { HeroHostMetaComponent } from './hero-host-meta.component';
|
||||
import { HeroIOComponent } from './hero-io.component';
|
||||
import { HeroComponent as HeroLifecycleComponent } from './hero-lifecycle.component';
|
||||
import { HeroQueriesComponent, ViewChildComponent, ContentChildComponent } from './hero-queries.component';
|
||||
import { HeroTitleComponent } from './hero-title.component';
|
||||
|
||||
import { DataService } from './data.service';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule
|
||||
],
|
||||
declarations: [
|
||||
AppComponent,
|
||||
ConfirmComponent,
|
||||
HeroComponent,
|
||||
HeroDIComponent,
|
||||
HeroDIInjectComponent,
|
||||
HeroDIInjectAdditionalComponent,
|
||||
HeroHostComponent, HeroHostMetaComponent,
|
||||
HeroIOComponent,
|
||||
HeroLifecycleComponent,
|
||||
HeroQueriesComponent, ViewChildComponent, ContentChildComponent,
|
||||
HeroTitleComponent
|
||||
],
|
||||
providers: [
|
||||
DataService,
|
||||
{ provide: 'heroName', useValue: 'Windstorm' }
|
||||
],
|
||||
bootstrap: [ AppComponent ],
|
||||
|
||||
// schemas: [ NO_ERRORS_SCHEMA ] // helpful for debugging!
|
||||
})
|
||||
export class AppModule { }
|
||||
|
||||
/* tslint:disable no-unused-variable */
|
||||
// #docregion ng2import
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import {
|
||||
LocationStrategy,
|
||||
HashLocationStrategy
|
||||
} from '@angular/common';
|
||||
// #enddocregion ng2import
|
|
@ -1,6 +0,0 @@
|
|||
<button (click)="onOkClick()">
|
||||
{{okMsg}}
|
||||
</button>
|
||||
<button (click)="onNotOkClick()">
|
||||
{{notOkMsg}}
|
||||
</button>
|
|
@ -1,21 +0,0 @@
|
|||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
|
||||
// #docregion
|
||||
@Component({
|
||||
selector: 'app-confirm',
|
||||
templateUrl: './confirm.component.html'
|
||||
})
|
||||
export class ConfirmComponent {
|
||||
@Input() okMsg = '';
|
||||
@Input('cancelMsg') notOkMsg = '';
|
||||
@Output() ok = new EventEmitter();
|
||||
@Output('cancel') notOk = new EventEmitter();
|
||||
|
||||
onOkClick() {
|
||||
this.ok.emit(true);
|
||||
}
|
||||
onNotOkClick() {
|
||||
this.notOk.emit(true);
|
||||
}
|
||||
}
|
||||
// #enddocregion
|
|
@ -1,10 +0,0 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable()
|
||||
export class DataService {
|
||||
constructor() { }
|
||||
|
||||
getHeroName() {
|
||||
return 'Windstorm';
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-di-inject-additional',
|
||||
template: `<hero-title title="Tour of Heroes"></hero-title>`
|
||||
})
|
||||
export class HeroComponent { }
|
|
@ -1,11 +0,0 @@
|
|||
import { Component, Inject } from '@angular/core';
|
||||
|
||||
// #docregion
|
||||
@Component({
|
||||
selector: 'hero-di-inject',
|
||||
template: `<h1>Hero: {{name}}</h1>`
|
||||
})
|
||||
export class HeroComponent {
|
||||
constructor(@Inject('heroName') private name: string) { }
|
||||
}
|
||||
// #enddocregion
|
|
@ -1,15 +0,0 @@
|
|||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
import { DataService } from './data.service';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-di',
|
||||
template: `<h1>Hero: {{name}}</h1>`
|
||||
})
|
||||
export class HeroComponent {
|
||||
name = '';
|
||||
constructor(dataService: DataService) {
|
||||
this.name = dataService.getHeroName();
|
||||
}
|
||||
}
|
||||
// #enddocregion
|
|
@ -1,44 +0,0 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
// #docregion
|
||||
@Component({
|
||||
selector: 'hero-host-meta',
|
||||
template: `
|
||||
<h1 [class.active]="active">Hero Host in Metadata</h1>
|
||||
<div>Heading clicks: {{clicks}}</div>
|
||||
`,
|
||||
host: {
|
||||
// HostBindings to the <hero-host-meta> element
|
||||
'[title]': 'title',
|
||||
'[class.heading]': 'headingClass',
|
||||
|
||||
// HostListeners on the entire <hero-host-meta> element
|
||||
'(click)': 'clicked()',
|
||||
'(mouseenter)': 'enter($event)',
|
||||
'(mouseleave)': 'leave($event)'
|
||||
},
|
||||
// Styles within (but excluding) the <hero-host-meta> element
|
||||
styles: ['.active {background-color: coral;}']
|
||||
})
|
||||
export class HeroHostMetaComponent {
|
||||
title = 'Hero Host in Metadata Tooltip';
|
||||
headingClass = true;
|
||||
|
||||
active = false;
|
||||
clicks = 0;
|
||||
|
||||
clicked() {
|
||||
this.clicks += 1;
|
||||
}
|
||||
|
||||
enter(event: Event) {
|
||||
this.active = true;
|
||||
this.headingClass = false;
|
||||
}
|
||||
|
||||
leave(event: Event) {
|
||||
this.active = false;
|
||||
this.headingClass = true;
|
||||
}
|
||||
}
|
||||
// #enddocregion
|
|
@ -1,39 +0,0 @@
|
|||
import { Component, HostBinding, HostListener } from '@angular/core';
|
||||
|
||||
// #docregion
|
||||
@Component({
|
||||
selector: 'hero-host',
|
||||
template: `
|
||||
<h1 [class.active]="active">Hero Host in Decorators</h1>
|
||||
<div>Heading clicks: {{clicks}}</div>
|
||||
`,
|
||||
// Styles within (but excluding) the <hero-host> element
|
||||
styles: ['.active {background-color: yellow;}']
|
||||
})
|
||||
export class HeroHostComponent {
|
||||
// HostBindings to the <hero-host> element
|
||||
@HostBinding() title = 'Hero Host in Decorators Tooltip';
|
||||
@HostBinding('class.heading') headingClass = true;
|
||||
|
||||
active = false;
|
||||
clicks = 0;
|
||||
|
||||
// HostListeners on the entire <hero-host> element
|
||||
@HostListener('click')
|
||||
clicked() {
|
||||
this.clicks += 1;
|
||||
}
|
||||
|
||||
@HostListener('mouseenter', ['$event'])
|
||||
enter(event: Event) {
|
||||
this.active = true;
|
||||
this.headingClass = false;
|
||||
}
|
||||
|
||||
@HostListener('mouseleave', ['$event'])
|
||||
leave(event: Event) {
|
||||
this.active = false;
|
||||
this.headingClass = true;
|
||||
}
|
||||
}
|
||||
// #enddocregion
|
|
@ -1,26 +0,0 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-io',
|
||||
template: `
|
||||
<app-confirm [okMsg]="'OK'"
|
||||
[cancelMsg]="'Cancel'"
|
||||
(ok)="onOk()"
|
||||
(cancel)="onCancel()">
|
||||
</app-confirm>
|
||||
<span *ngIf="okClicked">OK clicked</span>
|
||||
<span *ngIf="cancelClicked">Cancel clicked</span>
|
||||
`
|
||||
})
|
||||
export class HeroIOComponent {
|
||||
okClicked = false;
|
||||
cancelClicked = false;
|
||||
|
||||
onOk() {
|
||||
this.okClicked = true;
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
this.cancelClicked = true;
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
// #docregion
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-lifecycle',
|
||||
template: `<h1>Hero: {{name}}</h1>`
|
||||
})
|
||||
export class HeroComponent implements OnInit {
|
||||
name: string;
|
||||
ngOnInit() {
|
||||
// todo: fetch from server async
|
||||
setTimeout(() => this.name = 'Windstorm', 0);
|
||||
}
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
import {
|
||||
Component,
|
||||
ContentChild,
|
||||
Input,
|
||||
QueryList,
|
||||
ViewChildren
|
||||
} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'content-child',
|
||||
template: `
|
||||
<span class="content-child" *ngIf="active">
|
||||
Active
|
||||
</span>`
|
||||
})
|
||||
export class ContentChildComponent {
|
||||
active = false;
|
||||
|
||||
activate() {
|
||||
this.active = !this.active;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
// #docregion content
|
||||
@Component({
|
||||
selector: 'view-child',
|
||||
template: `
|
||||
<h2 [class.active]=active>
|
||||
{{hero.name}}
|
||||
<ng-content></ng-content>
|
||||
</h2>`,
|
||||
styles: ['.active {font-weight: bold; background-color: skyblue;}']
|
||||
})
|
||||
export class ViewChildComponent {
|
||||
@Input() hero: any;
|
||||
active = false;
|
||||
|
||||
@ContentChild(ContentChildComponent) content: ContentChildComponent;
|
||||
|
||||
activate() {
|
||||
this.active = !this.active;
|
||||
this.content.activate();
|
||||
}
|
||||
}
|
||||
// #enddocregion content
|
||||
|
||||
////////////////////
|
||||
|
||||
// #docregion view
|
||||
@Component({
|
||||
selector: 'hero-queries',
|
||||
template: `
|
||||
<view-child *ngFor="let hero of heroData" [hero]="hero">
|
||||
<content-child></content-child>
|
||||
</view-child>
|
||||
<button (click)="activate()">{{buttonLabel}} All</button>
|
||||
`
|
||||
})
|
||||
export class HeroQueriesComponent {
|
||||
active = false;
|
||||
heroData = [
|
||||
{id: 1, name: 'Windstorm'},
|
||||
{id: 2, name: 'LaughingGas'}
|
||||
];
|
||||
|
||||
@ViewChildren(ViewChildComponent) views: QueryList<ViewChildComponent>;
|
||||
|
||||
activate() {
|
||||
this.active = !this.active;
|
||||
this.views.forEach(
|
||||
view => view.activate()
|
||||
);
|
||||
}
|
||||
|
||||
// #docregion defined-property
|
||||
get buttonLabel() {
|
||||
return this.active ? 'Deactivate' : 'Activate';
|
||||
}
|
||||
// #enddocregion defined-property
|
||||
}
|
||||
// #enddocregion view
|
|
@ -1,4 +0,0 @@
|
|||
<!-- #docregion -->
|
||||
<h1>{{titlePrefix}} {{title}}</h1>
|
||||
<button (click)="ok()">OK</button>
|
||||
<p>{{ msg }}</p>
|
|
@ -1,21 +0,0 @@
|
|||
import { Attribute, Component, Inject, Optional } from '@angular/core';
|
||||
|
||||
// #docregion
|
||||
// #docregion templateUrl
|
||||
@Component({
|
||||
selector: 'hero-title',
|
||||
templateUrl: './hero-title.component.html'
|
||||
})
|
||||
// #enddocregion templateUrl
|
||||
export class HeroTitleComponent {
|
||||
msg = '';
|
||||
constructor(
|
||||
@Inject('titlePrefix') @Optional() private titlePrefix: string,
|
||||
@Attribute('title') private title: string
|
||||
) { }
|
||||
|
||||
ok() {
|
||||
this.msg = 'OK!';
|
||||
}
|
||||
}
|
||||
// #enddocregion
|
|
@ -1,15 +0,0 @@
|
|||
// #docregion
|
||||
// #docregion metadata
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-view',
|
||||
template: '<h1>{{title}}: {{getName()}}</h1>'
|
||||
})
|
||||
// #docregion appexport, class
|
||||
export class HeroComponent {
|
||||
title = 'Hero Detail';
|
||||
getName() {return 'Windstorm'; }
|
||||
}
|
||||
// #enddocregion appexport, class
|
||||
// #enddocregion metadata
|
|
@ -1,26 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<base href="/">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<title>TypeScript to JavaScript</title>
|
||||
|
||||
<!-- Polyfills -->
|
||||
<script src="node_modules/core-js/client/shim.min.js"></script>
|
||||
|
||||
<script src="node_modules/zone.js/dist/zone.js"></script>
|
||||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
|
||||
<script src="systemjs.config.js"></script>
|
||||
<script>
|
||||
System.import('main.js').catch(function(err){ console.error(err); });
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<my-app>Loading...</my-app>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,4 +0,0 @@
|
|||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
|
@ -609,6 +609,10 @@ Add the following _npm_ convenience script to the `package.json` so you can comp
|
|||
把下列*npm*便利脚本添加到`package.json`中,以便用一条命令就可以完成编译和Rollup打包工作。
|
||||
|
||||
|
||||
<code-example language="json">
|
||||
"build:aot": "ngc -p tsconfig-aot.json && rollup -c rollup-config.js",
|
||||
</code-example>
|
||||
|
||||
Open a terminal window and try it.
|
||||
|
||||
打开终端窗口,并试一下。
|
||||
|
@ -869,10 +873,15 @@ AOT编译假设上面介绍的一些支持文件都以准备好。
|
|||
</code-pane>
|
||||
</code-tabs>
|
||||
|
||||
Extend the `scripts` section of the `package.json` with these npm scripts:
|
||||
With the following npm script in the `scripts` section of the `package.json`, you can easily serve
|
||||
the AOT-compiled application:
|
||||
|
||||
使用下面的npm脚本,扩展`package.json`文件的`scripts`部分:
|
||||
|
||||
<code-example language="json">
|
||||
"serve:aot": "lite-server -c bs-config.aot.json",
|
||||
</code-example>
|
||||
|
||||
|
||||
Copy the AOT distribution files into the `/aot` folder with the node script:
|
||||
|
||||
|
@ -890,7 +899,7 @@ Copy the AOT distribution files into the `/aot` folder with the node script:
|
|||
|
||||
</div>
|
||||
|
||||
Now AOT-compile the app and launch it with the `lite-server`:
|
||||
Now AOT-compile the app and launch:
|
||||
|
||||
现在AOT编译应用,并使用`lite`服务器启动它:
|
||||
|
||||
|
|
|
@ -244,8 +244,8 @@ The documentation for the version prior to v.2.2.0 has been removed.
|
|||
|
||||
## 在“从TypeScript到JavaScript”增加ES6的描述 (2016-11-14)
|
||||
|
||||
The updated [TypeScript to JavaScript](guide/ts-to-js) guide
|
||||
now explains how to write apps in ES6/7
|
||||
The updated TypeScript to JavaScript guide (removed August 2017, PR #18694)
|
||||
explains how to write apps in ES6/7
|
||||
|
||||
更新了“[从TypeScript到JavaScript](guide/ts-to-js)”烹饪宝典,解释如何使用ES6/7编写应用
|
||||
|
||||
|
|
|
@ -0,0 +1,874 @@
|
|||
# Angular Metadata and AOT
|
||||
|
||||
The Angular **AOT compiler** turns your TypeScript source code into runnable JavaScript.
|
||||
As part of that process, the compiler extracts and interprets **metadata** about the parts of the application that Angular is supposed to manage.
|
||||
|
||||
You write metadata in a _subset_ of TypeScript. This guide explains why a subset is necessary, describes the subset constraints, and what happens when you step outside of those constraints.
|
||||
|
||||
## Angular metadata
|
||||
Angular metadata tells Angular how to construct instances of your application classes and interact with them at runtime.
|
||||
|
||||
You specify the metadata with **decorators** such as `@Component()` and `@Input()`.
|
||||
You also specify metadata implicitly in the constructor declarations of these decorated classes.
|
||||
|
||||
In the following example, the `@Component()` metadata object and the class constructor tell Angular how to create and display an instance of `TypicalComponent`.
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
selector: 'app-typical',
|
||||
template: '<div>A typical component for {{data.name}}</div>'
|
||||
)}
|
||||
export class TypicalComponent {
|
||||
@Input() data: TypicalData;
|
||||
constructor(private someService: SomeService) { ... }
|
||||
}
|
||||
```
|
||||
|
||||
The Angular compiler extracts the metadata _once_ and generates a _factory_ for `TypicalComponent`.
|
||||
When it needs to create a `TypicalComponent` instance, Angular calls the factory, which produces a new visual element, bound to a new instance of the component class with its injected dependency.
|
||||
|
||||
## Compile ahead-of-time (AOT)
|
||||
|
||||
You should use AOT to compile an application that must launch quickly.
|
||||
With AOT, there is no runtime compile step.
|
||||
The client doesn't need the compiler library at all and excluding it significantly reduces the total payload.
|
||||
The browser downloads a smaller set of safely-compiled, application module(s) and libraries that it can parse quickly and run almost immediately.
|
||||
|
||||
The AOT compiler produces a number of files, including the application JavaScript that ultimately runs in the browser. It then statically analyzes your source code and interprets the Angular metadata without actually running the application.
|
||||
|
||||
To compile the app, run the `ngc` stand-alone tool as part of your build process.
|
||||
When using the CLI, run the `ng build` command.
|
||||
|
||||
For more information on AOT, see [Ahead-of-Time Compilation](guide/aot-compiler).
|
||||
|
||||
## Metadata restrictions
|
||||
|
||||
Angular metadata expressions must conform to the following general constraints:
|
||||
|
||||
1. Limit [expression syntax](#expression-syntax) to the supported subset of JavaScript.
|
||||
2. Only reference exported symbols after [code folding](#folding).
|
||||
3. Only call [functions supported](#supported-functions) by the compiler.
|
||||
4. Decorated and data-bound class members must be public.
|
||||
|
||||
The next sections elaborate on these points.
|
||||
|
||||
## How AOT works
|
||||
|
||||
It helps to think of the AOT compiler as having two phases: a code analysis phase in which it simply records a representation of the source; and a code generation phase in which the compiler's `StaticReflector` handles the interpretation as well as places restrictions on what it interprets.
|
||||
|
||||
## Phase 1: analysis
|
||||
|
||||
The TypeScript compiler does some of the analytic work of the first phase. It emits the `.d.ts` _type definition files_ with type information that the AOT compiler needs to generate application code.
|
||||
|
||||
At the same time, the AOT **_collector_** analyzes the metadata recorded in the Angular decorators and outputs metadata information in **`.metadata.json`** files, one per `.d.ts` file.
|
||||
|
||||
You can think of `.metadata.json` as a diagram of the overall structure of a decorator's metadata, represented as an [abstract syntax tree (AST)](https://en.wikipedia.org/wiki/Abstract_syntax_tree).
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
Angular's [schema.ts](https://github.com/angular/angular/blob/master/packages/tsc-wrapped/src/schema.ts)
|
||||
describes the JSON format as a collection of TypeScript interfaces.
|
||||
|
||||
</div>
|
||||
|
||||
{@a expression-syntax}
|
||||
### Expression syntax
|
||||
|
||||
The _collector_ only understands a subset of JavaScript.
|
||||
Define metadata objects with the following limited syntax:
|
||||
|
||||
Syntax | Example
|
||||
-----------------------------------|-----------------------------------
|
||||
Literal object | `{cherry: true, apple: true, mincemeat: false}`
|
||||
Literal array | `['cherries', 'flour', 'sugar']`
|
||||
Spread in literal array | `['apples', 'flour', ...the_rest]`
|
||||
Calls | `bake(ingredients)`
|
||||
New | `new Oven()`
|
||||
Property access | `pie.slice`
|
||||
Array index | `ingredients[0]`
|
||||
Identifier reference | `Component`
|
||||
A template string | <code>`pie is ${multiplier} times better than cake`</code>
|
||||
Literal string | `'pi'`
|
||||
Literal number | `3.14153265`
|
||||
Literal boolean | `true`
|
||||
Literal null | `null`
|
||||
Supported prefix operator | `!cake`
|
||||
Supported Binary operator | `a + b`
|
||||
Conditional operator | `a ? b : c`
|
||||
Parentheses | `(a + b)`
|
||||
|
||||
If an expression uses unsupported syntax, the _collector_ writes an error node to the `.metadata.json` file. The compiler later reports the error if it needs that
|
||||
piece of metadata to generate the application code.
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
If you want `ngc` to report syntax errors immediately rather than produce a `.metadata.json` file with errors, set the `strictMetadataEmit` option in `tsconfig`.
|
||||
|
||||
```
|
||||
"angularCompilerOptions": {
|
||||
...
|
||||
"strictMetadataEmit" : true
|
||||
}
|
||||
```
|
||||
|
||||
Angular libraries have this option to ensure that all Angular `.metadata.json` files are clean and it is a best practice to do the same when building your own libraries.
|
||||
|
||||
</div>
|
||||
|
||||
{@a function-expression}
|
||||
{@a arror-functions}
|
||||
### No arrow functions
|
||||
|
||||
The AOT compiler does not support [function expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function)
|
||||
and [arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions), also called _lambda_ functions.
|
||||
|
||||
Consider the following component decorator:
|
||||
|
||||
```ts
|
||||
@Component({
|
||||
...
|
||||
providers: [{provide: server, useFactory: () => new Server()}]
|
||||
})
|
||||
```
|
||||
|
||||
The AOT _collector_ does not support the arrow function, `() => new Server()`, in a metadata expression.
|
||||
It generates an error node in place of the function.
|
||||
|
||||
When the compiler later interprets this node, it reports an error that invites you to turn the arrow function into an _exported function_.
|
||||
|
||||
You can fix the error by converting to this:
|
||||
|
||||
```ts
|
||||
export function serverFactory() {
|
||||
return new Server();
|
||||
}
|
||||
|
||||
@Component({
|
||||
...
|
||||
providers: [{provide: server, useFactory: serverFactory}]
|
||||
})
|
||||
```
|
||||
|
||||
### Limited function calls
|
||||
|
||||
The _collector_ can represent a function call or object creation with `new` as long as the syntax is valid. The _collector_ only cares about proper syntax.
|
||||
|
||||
But beware. The compiler may later refuse to generate a call to a _particular_ function or creation of a _particular_ object.
|
||||
The compiler only supports calls to a small set of functions and will use `new` for only a few designated classes. These functions and classes are in a table of [below](#supported-functions).
|
||||
|
||||
|
||||
### Folding
|
||||
{@a exported-symbols}
|
||||
The compiler can only resolve references to **_exported_** symbols.
|
||||
Fortunately, the _collector_ enables limited use of non-exported symbols through _folding_.
|
||||
|
||||
The _collector_ may be able to evaluate an expression during collection and record the result in the `.metadata.json` instead of the original expression.
|
||||
|
||||
For example, the _collector_ can evaluate the expression `1 + 2 + 3 + 4` and replace it with the result, `10`.
|
||||
|
||||
This process is called _folding_. An expression that can be reduced in this manner is _foldable_.
|
||||
|
||||
{@a var-declaration}
|
||||
The collector can evaluate references to
|
||||
module-local `const` declarations and initialized `var` and `let` declarations, effectively removing them from the `.metadata.json` file.
|
||||
|
||||
Consider the following component definition:
|
||||
|
||||
```ts
|
||||
const template = '<div>{{hero.name}}</div>';
|
||||
|
||||
@Component({
|
||||
selector: 'app-hero',
|
||||
template: template
|
||||
})
|
||||
class HeroComponent {
|
||||
@Input() hero: Hero;
|
||||
}
|
||||
```
|
||||
|
||||
The compiler could not refer to the `template` constant because it isn't exported.
|
||||
|
||||
But the _collector_ can _fold_ the `template` constant into the metadata definition by inlining its contents.
|
||||
The effect is the same as if you had written:
|
||||
|
||||
```TypeScript
|
||||
@Component({
|
||||
selector: 'app-hero',
|
||||
template: '<div>{{hero.name}}</div>'
|
||||
})
|
||||
class HeroComponent {
|
||||
@Input() hero: Hero;
|
||||
}
|
||||
```
|
||||
|
||||
There is no longer a reference to `template` and, therefore, nothing to trouble the compiler when it later interprets the _collector's_ output in `.metadata.json`.
|
||||
|
||||
You can take this example a step further by including the `template` constant in another expression:
|
||||
|
||||
```TypeScript
|
||||
const template = '<div>{{hero.name}}</div>';
|
||||
|
||||
@Component({
|
||||
selector: 'app-hero',
|
||||
template: template + '<div>{{hero.title}}</div>'
|
||||
})
|
||||
class HeroComponent {
|
||||
@Input() hero: Hero;
|
||||
}
|
||||
```
|
||||
|
||||
The _collector_ reduces this expression to its equivalent _folded_ string:
|
||||
|
||||
`'<div>{{hero.name}}</div><div>{{hero.title}}</div>'`.
|
||||
|
||||
#### Foldable syntax
|
||||
|
||||
The following table describes which expressions the _collector_ can and cannot fold:
|
||||
|
||||
Syntax | Foldable
|
||||
-----------------------------------|-----------------------------------
|
||||
Literal object | yes
|
||||
Literal array | yes
|
||||
Spread in literal array | no
|
||||
Calls | no
|
||||
New | no
|
||||
Property access | yes, if target is foldable
|
||||
Array index | yes, if target and index are foldable
|
||||
Identifier reference | yes, if it is a reference to a local
|
||||
A template with no substitutions | yes
|
||||
A template with substitutions | yes, if the substitutions are foldable
|
||||
Literal string | yes
|
||||
Literal number | yes
|
||||
Literal boolean | yes
|
||||
Literal null | yes
|
||||
Supported prefix operator | yes, if operand is foldable
|
||||
Supported binary operator | yes, if both left and right are foldable
|
||||
Conditional operator | yes, if condition is foldable
|
||||
Parentheses | yes, if the expression is foldable
|
||||
|
||||
If an expression is not foldable, the collector writes it to `.metadata.json` as an [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree) for the compiler to resolve.
|
||||
|
||||
|
||||
## Phase 2: code generation
|
||||
|
||||
The _collector_ makes no attempt to understand the metadata that it collects and outputs to `.metadata.json`. It represents the metadata as best it can and records errors when it detects a metadata syntax violation.
|
||||
|
||||
It's the compiler's job to interpret the `.metadata.json` in the code generation phase.
|
||||
|
||||
The compiler understands all syntax forms that the _collector_ supports, but it may reject _syntactically_ correct metadata if the _semantics_ violate compiler rules.
|
||||
|
||||
The compiler can only reference _exported symbols_.
|
||||
|
||||
Decorated component class members must be public. You cannot make an `@Input()` property private or internal.
|
||||
|
||||
Data bound properties must also be public.
|
||||
|
||||
```TypeScript
|
||||
// BAD CODE - title is private
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
template: '<h1>{{title}}</h1>'
|
||||
})
|
||||
class AppComponent {
|
||||
private title = 'My App'; // Bad
|
||||
}
|
||||
```
|
||||
|
||||
{@a supported-functions}
|
||||
Most importantly, the compiler only generates code to create instances of certain classes, support certain decorators, and call certain functions from the following lists.
|
||||
|
||||
|
||||
### New instances
|
||||
|
||||
The compiler only allows metadata that create instances of these Angular classes.
|
||||
|
||||
Class | Module
|
||||
-----------------|--------------
|
||||
`OpaqueToken` | `@angular/core`
|
||||
`InjectionToken` | `@angular/core`
|
||||
|
||||
|
||||
### Annotations/Decorators
|
||||
|
||||
The compiler only supports metadata for these Angular decorators.
|
||||
|
||||
Decorator | Module
|
||||
------------------|--------------
|
||||
`Attribute` | `@angular/core`
|
||||
`Component` | `@angular/core`
|
||||
`ContentChild` | `@angular/core`
|
||||
`ContentChildren` | `@angular/core`
|
||||
`Directive` | `@angular/core`
|
||||
`Host` | `@angular/core`
|
||||
`HostBinding` | `@angular/core`
|
||||
`HostListener` | `@angular/core`
|
||||
`Inject` | `@angular/core`
|
||||
`Injectable` | `@angular/core`
|
||||
`Input` | `@angular/core`
|
||||
`NgModule` | `@angular/core`
|
||||
`Optional` | `@angular/core`
|
||||
`Output` | `@angular/core`
|
||||
`Pipe` | `@angular/core`
|
||||
`Self` | `@angular/core`
|
||||
`SkipSelf` | `@angular/core`
|
||||
`ViewChild` | `@angular/core`
|
||||
|
||||
|
||||
### Macro-functions and macro-static methods
|
||||
|
||||
The compiler also supports _macros_ in the form of functions or static
|
||||
methods that return an expression.
|
||||
|
||||
For example, consider the following function:
|
||||
|
||||
```TypeScript
|
||||
export function wrapInArray<T>(value: T): T[] {
|
||||
return [value];
|
||||
}
|
||||
```
|
||||
|
||||
You can call the `wrapInArray` in a metadata definition because it returns the value of an expression that conforms to the compiler's restrictive JavaScript subset.
|
||||
|
||||
You might use `wrapInArray()` like this:
|
||||
|
||||
```TypeScript
|
||||
@NgModule({
|
||||
declarations: wrapInArray(TypicalComponent)
|
||||
})
|
||||
class TypicalModule {}
|
||||
```
|
||||
|
||||
The compiler treats this usage as if you had written:
|
||||
|
||||
```TypeScript
|
||||
@NgModule({
|
||||
declarations: [TypicalComponent]
|
||||
})
|
||||
class TypicalModule {}
|
||||
```
|
||||
|
||||
The collector is simplistic in its determination of what qualifies as a macro
|
||||
function; it can only contain a single `return` statement.
|
||||
|
||||
The Angular [`RouterModule`](api/router/RouterModule) exports two macro static methods, `forRoot` and `forChild`, to help declare root and child routes.
|
||||
Review the [source code](https://github.com/angular/angular/blob/master/packages/router/src/router_module.ts#L139 "RouterModule.forRoot source code")
|
||||
for these methods to see how macros can simplify configuration of complex Angular modules.
|
||||
|
||||
## Metadata Errors
|
||||
|
||||
The following are metadata errors you may encounter, with explanations and suggested corrections.
|
||||
|
||||
[Expression form not supported](#expression-form-not-supported)<br>
|
||||
[Reference to a local (non-exported) symbol](#reference-to-a-local-symbol)<br>
|
||||
[Only initialized variables and constants](#only-initialized-variables)<br>
|
||||
[Reference to a non-exported class](#reference-to-a-non-exported-class)<br>
|
||||
[Reference to a non-exported function](#reference-to-a-non-exported-function)<br>
|
||||
[Function calls are not supported](#function-calls-not-supported)<br>
|
||||
[Destructured variable or constant not supported](#destructured-variable-not-supported)<br>
|
||||
[Could not resolve type](#could-not-resolve-type)<br>
|
||||
[Name expected](#name-expected)<br>
|
||||
[Unsupported enum member name](#unsupported-enum-member-name)<br>
|
||||
[Tagged template expressions are not supported](#tagged-template-expressions-not-supported)<br>
|
||||
[Symbol reference expected](#symbol-reference-expected)<br>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3 class="no-toc">Expression form not supported</h3>
|
||||
|
||||
The compiler encountered an expression it didn't understand while evalutating Angular metadata.
|
||||
|
||||
Language features outside of the compiler's [restricted expression syntax](#expression-syntax)
|
||||
can produce this error, as seen in the following example:
|
||||
|
||||
```
|
||||
// ERROR
|
||||
export class Fooish { ... }
|
||||
...
|
||||
const prop = typeof Fooish; // typeof is not valid in metadata
|
||||
...
|
||||
// bracket notation is not valid in metadata
|
||||
{ provide: 'token', useValue: { [prop]: 'value' } };
|
||||
...
|
||||
```
|
||||
|
||||
You can use `typeof` and bracket notation in normal application code.
|
||||
You just can't use those features within expressions that define Angular metadata.
|
||||
|
||||
Avoid this error by sticking to the compiler's [restricted expression syntax](#expression-syntax)
|
||||
when writing Angular metadata
|
||||
and be wary of new or unusual TypeScript features.
|
||||
|
||||
<hr>
|
||||
|
||||
{@a reference-to-a-local-symbol}
|
||||
<h3 class="no-toc">Reference to a local (non-exported) symbol</h3>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
_Reference to a local (non-exported) symbol 'symbol name'. Consider exporting the symbol._
|
||||
|
||||
</div>
|
||||
|
||||
The compiler encountered a referenced to a locally defined symbol that either wasn't exported or wasn't initialized.
|
||||
|
||||
Here's a `provider` example of the problem.
|
||||
|
||||
```
|
||||
// ERROR
|
||||
let foo: number; // neither exported nor initialized
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: ... ,
|
||||
providers: [
|
||||
{ provide: Foo, useValue: foo }
|
||||
]
|
||||
})
|
||||
export class MyComponent {}
|
||||
```
|
||||
The compiler generates the component factory, which includes the `useValue` provider code, in a separate module. _That_ factory module can't reach back to _this_ source module to access the local (non-exported) `foo` variable.
|
||||
|
||||
You could fix the problem by initializing `foo`.
|
||||
|
||||
```
|
||||
let foo = 42; // initialized
|
||||
```
|
||||
|
||||
The compiler will [fold](#folding) the expression into the provider as if you had written this.
|
||||
|
||||
```
|
||||
providers: [
|
||||
{ provide: Foo, useValue: 42 }
|
||||
]
|
||||
```
|
||||
|
||||
Alternatively, you can fix it by exporting `foo` with the expectation that `foo` will be assigned at runtime when you actually know its value.
|
||||
|
||||
```
|
||||
// CORRECTED
|
||||
export let foo: number; // exported
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: ... ,
|
||||
providers: [
|
||||
{ provide: Foo, useValue: foo }
|
||||
]
|
||||
})
|
||||
export class MyComponent {}
|
||||
```
|
||||
|
||||
Adding `export` often works for variables referenced in metadata such as `providers` and `animations` because the compiler can generate _references_ to the exported variables in these expressions. It doesn't need the _values_ of those variables.
|
||||
|
||||
Adding `export` doesn't work when the compiler needs the _actual value_
|
||||
in order to generate code.
|
||||
For example, it doesn't work for the `template` property.
|
||||
|
||||
```
|
||||
// ERROR
|
||||
export let someTemplate: string; // exported but not initialized
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: someTemplate
|
||||
})
|
||||
export class MyComponent {}
|
||||
```
|
||||
|
||||
The compiler needs the value of the `template` property _right now_ to generate the component factory.
|
||||
The variable reference alone is insufficient.
|
||||
Prefixing the declaration with `export` merely produces a new error, "[`Only initialized variables and constants can be referenced`](#only-initialized-variables)".
|
||||
|
||||
<hr>
|
||||
|
||||
{@a only-initialized-variables}
|
||||
<h3 class="no-toc">Only initialized variables and constants</h3>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
_Only initialized variables and constants can be referenced because the value of this variable is needed by the template compiler._
|
||||
|
||||
</div>
|
||||
|
||||
The compiler found a reference to an exported variable or static field that wasn't initialized.
|
||||
It needs the value of that variable to generate code.
|
||||
|
||||
The following example tries to set the component's `template` property to the value of
|
||||
the exported `someTemplate` variable which is declared but _unassigned_.
|
||||
|
||||
```
|
||||
// ERROR
|
||||
export let someTemplate: string;
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: someTemplate
|
||||
})
|
||||
export class MyComponent {}
|
||||
```
|
||||
|
||||
You'd also get this error if you imported `someTemplate` from some other module and neglected to initialize it there.
|
||||
|
||||
```
|
||||
// ERROR - not initialized there either
|
||||
import { someTemplate } from './config';
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: someTemplate
|
||||
})
|
||||
export class MyComponent {}
|
||||
```
|
||||
|
||||
The compiler cannot wait until runtime to get the template information.
|
||||
It must statically derive the value of the `someTemplate` variable from the source code
|
||||
so that it can generate the component factory, which includes
|
||||
instructions for building the element based on the template.
|
||||
|
||||
To correct this error, provide the initial value of the variable in an initializer clause _on the same line_.
|
||||
|
||||
```
|
||||
// CORRECTED
|
||||
export let someTemplate = '<h1>Greetings from Angular</h1>';
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: someTemplate
|
||||
})
|
||||
export class MyComponent {}
|
||||
```
|
||||
|
||||
<hr>
|
||||
|
||||
<h3 class="no-toc">Reference to a non-exported class</h3>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
_Reference to a non-exported class <class name>. Consider exporting the class._
|
||||
|
||||
</div>
|
||||
|
||||
Metadata referenced a class that wasn't exported.
|
||||
|
||||
For example, you may have defined a class and used it as an injection token in a providers array
|
||||
but neglected to export that class.
|
||||
|
||||
```
|
||||
// ERROR
|
||||
abstract class MyStrategy { }
|
||||
|
||||
...
|
||||
providers: [
|
||||
{ provide: MyStrategy, useValue: ... }
|
||||
]
|
||||
...
|
||||
```
|
||||
|
||||
Angular generates a class factory in a separate module and that
|
||||
factory [can only access exported classes](#exported-symbols).
|
||||
To correct this error, export the referenced class.
|
||||
|
||||
```
|
||||
// CORRECTED
|
||||
export abstract class MyStrategy { }
|
||||
|
||||
...
|
||||
providers: [
|
||||
{ provide: MyStrategy, useValue: ... }
|
||||
]
|
||||
...
|
||||
```
|
||||
<hr>
|
||||
|
||||
<h3 class="no-toc">Reference to a non-exported function</h3>
|
||||
|
||||
Metadata referenced a function that wasn't exported.
|
||||
|
||||
For example, you may have set a providers `useFactory` property to a locally defined function that you neglected to export.
|
||||
|
||||
```
|
||||
// ERROR
|
||||
function myStrategy() { ... }
|
||||
|
||||
...
|
||||
providers: [
|
||||
{ provide: MyStrategy, useFactory: myStrategy }
|
||||
]
|
||||
...
|
||||
```
|
||||
|
||||
Angular generates a class factory in a separate module and that
|
||||
factory [can only access exported functions](#exported-symbols).
|
||||
To correct this error, export the function.
|
||||
|
||||
```
|
||||
// CORRECTED
|
||||
export function myStrategy() { ... }
|
||||
|
||||
...
|
||||
providers: [
|
||||
{ provide: MyStrategy, useFactory: myStrategy }
|
||||
]
|
||||
...
|
||||
```
|
||||
<hr>
|
||||
|
||||
{@a function-calls-not-supported}
|
||||
<h3 class="no-toc">Function calls are not supported</h3>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
_Function calls are not supported. Consider replacing the function or lambda with a reference to an exported function._
|
||||
|
||||
</div>
|
||||
|
||||
The compiler does not currently support [function expressions or lambda functions](#function-expression).
|
||||
For example, you cannot set a provider's `useFactory` to an anonymous function or arrow function like this.
|
||||
|
||||
```
|
||||
// ERROR
|
||||
...
|
||||
providers: [
|
||||
{ provide: MyStrategy, useFactory: function() { ... } },
|
||||
{ provide: OtherStrategy, useFactory: () => { ... } }
|
||||
]
|
||||
...
|
||||
```
|
||||
You also get this error if you call a function or method in a provider's `useValue`.
|
||||
```
|
||||
// ERROR
|
||||
import { calculateValue } from './utilities';
|
||||
|
||||
...
|
||||
providers: [
|
||||
{ provide: SomeValue, useValue: calculateValue() }
|
||||
]
|
||||
...
|
||||
```
|
||||
|
||||
To correct this error, export a function from the module and refer to the function in a `useFactory` provider instead.
|
||||
|
||||
<code-example linenums="false">
|
||||
// CORRECTED
|
||||
import { calculateValue } from './utilities';
|
||||
|
||||
export function myStrategy() { ... }
|
||||
export function otherStrategy() { ... }
|
||||
export function someValueFactory() {
|
||||
return calculateValue();
|
||||
}
|
||||
...
|
||||
providers: [
|
||||
{ provide: MyStrategy, useFactory: myStrategy },
|
||||
{ provide: OtherStrategy, useFactory: otherStrategy },
|
||||
{ provide: SomeValue, useFactory: someValueFactory }
|
||||
]
|
||||
...
|
||||
</code-example>
|
||||
|
||||
<hr>
|
||||
|
||||
{@a destructured-variable-not-supported}
|
||||
<h3 class="no-toc">Destructured variable or constant not supported</h3>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
_Referencing an exported destructured variable or constant is not supported by the template compiler. Consider simplifying this to avoid destructuring._
|
||||
|
||||
</div>
|
||||
|
||||
The compiler does not support references to variables assigned by [destructuring](https://www.typescriptlang.org/docs/handbook/variable-declarations.html#destructuring).
|
||||
|
||||
For example, you cannot write something like this:
|
||||
|
||||
<code-example linenums="false">
|
||||
// ERROR
|
||||
import { configuration } from './configuration';
|
||||
|
||||
// destructured assignment to foo and bar
|
||||
const {foo, bar} = configuration;
|
||||
...
|
||||
providers: [
|
||||
{provide: Foo, useValue: foo},
|
||||
{provide: Bar, useValue: bar},
|
||||
]
|
||||
...
|
||||
</code-example>
|
||||
|
||||
To correct this error, refer to non-destructured values.
|
||||
|
||||
<code-example linenums="false">
|
||||
// CORRECTED
|
||||
import { configuration } from './configuration';
|
||||
...
|
||||
providers: [
|
||||
{provide: Foo, useValue: configuration.foo},
|
||||
{provide: Bar, useValue: configuration.bar},
|
||||
]
|
||||
...
|
||||
</code-example>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3 class="no-toc">Could not resolve type</h3>
|
||||
|
||||
The compiler encountered a type and can't determine which module exports that type.
|
||||
|
||||
This can happen if you refer to an ambient type.
|
||||
For example, the `Window` type is an ambiant type declared in the global `.d.ts` file.
|
||||
|
||||
You'll get an error if you reference it in the component constructor,
|
||||
which the compiler must statically analyze.
|
||||
|
||||
```
|
||||
// ERROR
|
||||
@Component({ })
|
||||
export class MyComponent {
|
||||
constructor (private win: Window) { ... }
|
||||
}
|
||||
```
|
||||
TypeScript understands ambiant types so you don't import them.
|
||||
The Angular compiler does not understand a type that you neglect to export or import.
|
||||
|
||||
In this case, the compiler doesn't understand how to inject something with the `Window` token.
|
||||
|
||||
Do not refer to ambient types in metadata expressions.
|
||||
|
||||
If you must inject an instance of an ambiant type,
|
||||
you can finesse the problem in four steps:
|
||||
|
||||
1. Create an injection token for an instance of the ambiant type.
|
||||
1. Create a factory function that returns that instance.
|
||||
1. Add a `useFactory` provider with that factory function.
|
||||
1. Use `@Inject` to inject the instance.
|
||||
|
||||
Here's an illustrative example.
|
||||
|
||||
<code-example linenums="false">
|
||||
// CORRECTED
|
||||
import { Inject } from '@angular/core';
|
||||
|
||||
export const WINDOW = new InjectionToken('Window');
|
||||
export function _window() { return window; }
|
||||
|
||||
@Component({
|
||||
...
|
||||
providers: [
|
||||
{ provide: WINDOW, useFactory: _window }
|
||||
]
|
||||
})
|
||||
export class MyComponent {
|
||||
constructor (@Inject(WINDOW) private win: Window) { ... }
|
||||
}
|
||||
</code-example>
|
||||
|
||||
The `Window` type in the constructor is no longer a problem for the compiler because it
|
||||
uses the `@Inject(WINDOW)` to generate the injection code.
|
||||
|
||||
Angular does something similar with the `DOCUMENT` token so you can inject the browser's `document` object (or an abstraction of it, depending upon the platform in which the application runs).
|
||||
|
||||
<code-example linenums="false">
|
||||
import { Inject } from '@angular/core';
|
||||
import { DOCUMENT } from '@angular/platform-browser';
|
||||
|
||||
@Component({ ... })
|
||||
export class MyComponent {
|
||||
constructor (@Inject(DOCUMENT) private doc: Document) { ... }
|
||||
}
|
||||
</code-example>
|
||||
<hr>
|
||||
|
||||
<h3 class="no-toc">Name expected</h3>
|
||||
|
||||
The compiler expected a name in an expression it was evaluating.
|
||||
This can happen if you use a number as a property name as in the following example.
|
||||
|
||||
```
|
||||
// ERROR
|
||||
provider: [{ provide: Foo, useValue: { 0: 'test' } }]
|
||||
```
|
||||
|
||||
Change the name of the property to something non-numeric.
|
||||
|
||||
```
|
||||
// CORRECTED
|
||||
provider: [{ provide: Foo, useValue: { '0': 'test' } }]
|
||||
```
|
||||
|
||||
<hr>
|
||||
|
||||
<h3 class="no-toc">Unsupported enum member name</h3>
|
||||
|
||||
Angular couldn't determine the value of the [enum member](https://www.typescriptlang.org/docs/handbook/enums.html)
|
||||
that you referenced in metadata.
|
||||
|
||||
The compiler can understand simple enum values but not complex values such as those derived from computed properties.
|
||||
|
||||
<code-example linenums="false">
|
||||
// ERROR
|
||||
enum Colors {
|
||||
Red = 1,
|
||||
White,
|
||||
Blue = "Blue".length // computed
|
||||
}
|
||||
|
||||
...
|
||||
providers: [
|
||||
{ provide: BaseColor, useValue: Colors.White } // ok
|
||||
{ provide: DangerColor, useValue: Colors.Red } // ok
|
||||
{ provide: StrongColor, useValue: Colors.Blue } // bad
|
||||
]
|
||||
...
|
||||
</code-example>
|
||||
|
||||
Avoid referring to enums with complicated initializers or computed properties.
|
||||
|
||||
<hr>
|
||||
|
||||
{@a tagged-template-expressions-not-supported}
|
||||
<h3 class="no-toc">Tagged template expressions are not supported</h3>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
_Tagged template expressions are not supported in metadata._
|
||||
|
||||
</div>
|
||||
|
||||
The compiler encountered a JavaScript ES2015 [tagged template expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_template_literals) such as,
|
||||
```
|
||||
// ERROR
|
||||
const expression = 'funky';
|
||||
const raw = String.raw`A tagged template ${expression} string`;
|
||||
...
|
||||
template: '<div>' + raw + '</div>'
|
||||
...
|
||||
```
|
||||
[`String.raw()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/raw)
|
||||
is a _tag function_ native to JavaScript ES2015.
|
||||
|
||||
The AOT compiler does not support tagged template expressions; avoid them in metadata expressions.
|
||||
|
||||
<hr>
|
||||
|
||||
<h3 class="no-toc">Symbol reference expected</h3>
|
||||
|
||||
The compiler expected a reference to a symbol at the location specified in the error message.
|
||||
|
||||
This error can occur if you use an expression in the `extends` clause of a class.
|
||||
|
||||
<!--
|
||||
|
||||
Chuck: After reviewing your PR comment I'm still at a loss. See [comment there](https://github.com/angular/angular/pull/17712#discussion_r132025495).
|
||||
|
||||
-->
|
||||
|
||||
## Conclusion
|
||||
|
||||
This page covered:
|
||||
|
||||
* What the AOT compiler does.
|
||||
* Why metadata must be written in a subset of JavaScript.
|
||||
* What that subset is.
|
||||
* Other restrictions on metadata definition.
|
||||
* Macro-functions and macro-static methods.
|
||||
* Compiler errors related to metadata.
|
|
@ -1673,10 +1673,10 @@ The default form displays a nameless hero with no addresses.
|
|||
默认的表单显示一个无地址的无名英雄。
|
||||
|
||||
You need a method to populate (or repopulate) the _secretLairs_ with actual hero addresses whenever
|
||||
the parent `HeroListComponent` sets the `HeroListComponent.hero` input property to a new `Hero`.
|
||||
the parent `HeroListComponent` sets the `HeroDetailComponent.hero` input property to a new `Hero`.
|
||||
|
||||
我们需要一个方法来用实际英雄的地址填充(或重新填充)`secretLairs`,
|
||||
而不用管父组件`HeroListComponent`何时把输入属性`HeroListComponent.hero`设置为一个新的`Hero`。
|
||||
而不用管父组件`HeroListComponent`何时把输入属性`HeroDetailComponent.hero`设置为一个新的`Hero`。
|
||||
|
||||
The following `setAddresses` method replaces the _secretLairs_ `FormArray` with a new `FormArray`,
|
||||
initialized by an array of hero address `FormGroups`.
|
||||
|
|
|
@ -2140,7 +2140,7 @@ They are usually applied to elements as if they were HTML attributes, hence the
|
|||
它们通常会作为HTML属性的名称而应用在元素上。
|
||||
|
||||
Many details are covered in the [_Attribute Directives_](guide/attribute-directives) guide.
|
||||
Many NgMdules such as the [`RouterModule`](guide/router "Routing and Navigation")
|
||||
Many NgModules such as the [`RouterModule`](guide/router "Routing and Navigation")
|
||||
and the [`FormsModule`](guide/forms "Forms") define their own attribute directives.
|
||||
This section is an introduction to the most commonly used attribute directives:
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue