docs(cb-ts-to-js): add cookbook about applying TypeScript examples to ES5

closes #893
This commit is contained in:
Tero Parviainen 2016-03-16 18:01:33 +02:00 committed by Ward Bell
parent 1184b57740
commit 7f11a6ce05
42 changed files with 1475 additions and 16 deletions

View File

@ -0,0 +1,73 @@
describe('TypeScript to Javascript tests', function () {
beforeAll(function () {
browser.get('');
});
it('should display the basic component example', function () {
testTag('hero-view', 'Hero: 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 () {
var app = element(by.css('hero-di-inject-additional'));
var h1 = app.element(by.css('h1'));
var okMsg = app.element(by.css('.ok-msg'));
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 () {
var app = element(by.css('hero-io'));
var confirmComponent = app.element(by.css('my-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() {
var app = element(by.css('heroes-bindings'));
var h1 = app.element(by.css('h1'));
expect(app.getAttribute('class')).toBe('heading');
expect(app.getAttribute('title')).toBe('Tooltip content');
h1.click();
expect(h1.getAttribute('class')).toBe('active');
h1.click();
browser.actions().doubleClick(h1).perform();
expect(h1.getAttribute('class')).toBe('active');
});
it('should support content and view queries', function() {
var app = element(by.css('heroes-queries'));
var windstorm = app.element(by.css('hero:first-child'));
app.element(by.buttonText('Activate')).click();
expect(windstorm.element(by.css('h2')).getAttribute('class')).toBe('active');
expect(windstorm.element(by.css('active-label')).getText()).toBe('Active');
});
function testTag(selector, expectedText) {
var component = element(by.css(selector));
expect(component.getText()).toBe(expectedText);
}
});

View File

@ -0,0 +1,13 @@
(function(app) {
function DataService() {
}
DataService.annotations = [
new ng.core.Injectable()
];
DataService.prototype.getHeroName = function() {
return 'Windstorm';
};
app.DataService = DataService;
})(window.app = window.app || {});

View File

@ -0,0 +1,47 @@
(function(app) {
// #docregion
var TitleComponent = ng.core.Component({
selector: 'hero-title',
template:
'<h1>{{titlePrefix}} {{title}}</h1>' +
'<button (click)="ok()">OK</button>' +
'<ng-content></ng-content>'
}).Class({
constructor: [
[
new ng.core.Optional(),
new ng.core.Inject('titlePrefix')
],
new ng.core.Attribute('title'),
[
new ng.core.Query('okMsg'),
ng.core.ElementRef
],
function(titlePrefix, title, msg) {
this.titlePrefix = titlePrefix;
this.title = title;
this.msg = msg;
}
],
ok: function() {
var msgEl =
this.msg.first.nativeElement;
msgEl.textContent = 'OK!';
}
});
// #enddocregion
var AppComponent = ng.core.Component({
selector: 'hero-di-inject-additional',
template: '<hero-title title="Tour of Heroes">' +
'<span #okMsg class="ok-msg"></span>' +
'</hero-title>',
directives: [TitleComponent]
}).Class({
constructor: function() { }
});
app.HeroDIInjectAdditionalComponent = AppComponent;
})(window.app = window.app || {});

View File

@ -0,0 +1,39 @@
(function(app) {
// #docregion parameters
function HeroComponent(name) {
this.name = name;
}
HeroComponent.parameters = [
'heroName'
];
HeroComponent.annotations = [
new ng.core.Component({
selector: 'hero-di-inject',
template: '<h1>Hero: {{name}}</h1>'
})
];
// #enddocregion parameters
app.HeroDIInjectComponent = HeroComponent;
})(window.app = window.app || {});
(function(app) {
// #docregion ctor
var HeroComponent = ng.core.Component({
selector: 'hero-di-inline2',
template: '<h1>Hero: {{name}}</h1>'
})
.Class({
constructor:
[new ng.core.Inject('heroName'),
function(name) {
this.name = name;
}]
});
// #enddocregion ctor
app.HeroDIInjectComponent2 = HeroComponent;
})(window.app = window.app || {});

View File

@ -0,0 +1,16 @@
(function(app) {
// #docregion
var HeroComponent = ng.core.Component({
selector: 'hero-di-inline',
template: '<h1>Hero: {{name}}</h1>'
})
.Class({
constructor:
[app.DataService,
function(service) {
this.name = service.getHeroName();
}]
});
// #enddocregion
app.HeroDIInlineComponent = HeroComponent;
})(window.app = window.app || {});

View File

@ -0,0 +1,20 @@
(function(app) {
// #docregion
app.HeroDIComponent = HeroComponent;
function HeroComponent(dataService) {
this.name = dataService.getHeroName();
}
HeroComponent.parameters = [
app.DataService
];
HeroComponent.annotations = [
new ng.core.Component({
selector: 'hero-di',
template: '<h1>Hero: {{name}}</h1>'
})
];
// #enddocregion
})(window.app = window.app || {});

View File

@ -0,0 +1,23 @@
// #docplaster
// #docregion appexport
(function(app) {
// #docregion component
var HeroComponent = ng.core.Component({
selector: 'hero-view-2',
template:
'<h1>Name: {{getName()}}</h1>',
})
.Class({
constructor: function() {
},
getName: function() {
return 'Windstorm';
}
});
// #enddocregion component
app.HeroComponentDsl = HeroComponent;
})(window.app = window.app || {});
// #enddocregion appexport

View File

@ -0,0 +1,57 @@
(function(app) {
// #docregion
var ConfirmComponent = ng.core.Component({
selector: 'my-confirm',
inputs: [
'okMsg',
'notOkMsg: cancelMsg'
],
outputs: [
'ok',
'notOk: cancel'
],
template:
'<button (click)="onOkClick()">' +
'{{okMsg}}' +
'</button>' +
'<button (click)="onNotOkClick()">' +
'{{notOkMsg}}' +
'</button>'
}).Class({
constructor: function() {
this.ok = new ng.core.EventEmitter();
this.notOk = new ng.core.EventEmitter();
},
onOkClick: function() {
this.ok.next(true);
},
onNotOkClick: function() {
this.notOk.next(true);
}
});
// #enddocregion
function AppComponent() {
}
AppComponent.annotations = [
new ng.core.Component({
selector: 'hero-io',
template: '<my-confirm [okMsg]="\'OK\'"' +
'[cancelMsg]="\'Cancel\'"' +
'(ok)="onOk()"' +
'(cancel)="onCancel()">' +
'</my-confirm>' +
'<span *ngIf="okClicked">OK clicked</span>' +
'<span *ngIf="cancelClicked">Cancel clicked</span>',
directives: [ConfirmComponent]
})
];
AppComponent.prototype.onOk = function() {
this.okClicked = true;
}
AppComponent.prototype.onCancel = function() {
this.cancelClicked = true;
}
app.HeroIOComponent = AppComponent;
})(window.app = window.app || {});

View File

@ -0,0 +1,21 @@
// #docplaster
(function(app) {
// #docregion
function HeroComponent() {}
// #enddocregion
HeroComponent.annotations = [
new ng.core.Component({
selector: 'hero-lifecycle',
template: '<h1>Hero: {{name}}</h1>'
})
];
// #docregion
HeroComponent.prototype.ngOnInit =
function() {
this.name = 'Windstorm';
};
// #enddocregion
app.HeroLifecycleComponent = HeroComponent;
})(window.app = window.app || {});

View File

@ -0,0 +1,32 @@
// #docplaster
// #docregion appexport
(function(app) {
// #enddocregion appexport
// #docregion metadata
// #docregion appexport
// #docregion constructorproto
function HeroComponent() {
this.title = "Hero Detail";
}
// #enddocregion constructorproto
// #enddocregion appexport
HeroComponent.annotations = [
new ng.core.Component({
selector: 'hero-view',
template:
'<h1>Hero: {{getName()}}</h1>'
})
];
// #docregion constructorproto
HeroComponent.prototype.getName =
function() {return 'Windstorm';};
// #enddocregion constructorproto
// #enddocregion metadata
// #docregion appexport
app.HeroComponent = HeroComponent;
})(window.app = window.app || {});
// #enddocregion appexport

View File

@ -0,0 +1,30 @@
(function(app) {
// #docregion
var HeroesComponent = ng.core.Component({
selector: 'heroes-bindings',
template: '<h1 [class.active]="active">' +
'Tour of Heroes' +
'</h1>',
host: {
'[title]': 'title',
'[class.heading]': 'hClass',
'(click)': 'clicked()',
'(dblclick)': 'doubleClicked($event)'
}
}).Class({
constructor: function() {
this.title = 'Tooltip content';
this.hClass = true;
},
clicked: function() {
this.active = !this.active;
},
doubleClicked: function(evt) {
this.active = true;
}
});
// #enddocregion
app.HeroesHostBindingsComponent = HeroesComponent;
})(window.app = window.app || {});

View File

@ -0,0 +1,73 @@
(function(app) {
var ActiveLabelComponent = ng.core.Component({
selector: 'active-label',
template: '<span class="active-label"' +
'*ngIf="active">' +
'Active' +
'</span>'
}).Class({
constructor: function() { },
activate: function() {
this.active = true;
}
});
// #docregion content
var HeroComponent = ng.core.Component({
selector: 'hero',
template: '<h2 [class.active]=active>' +
'{{hero.name}} ' +
'<ng-content></ng-content>' +
'</h2>',
inputs: ['hero'],
queries: {
label: new ng.core.ContentChild(
ActiveLabelComponent)
}
}).Class({
constructor: function() { },
activate: function() {
this.active = true;
this.label.activate();
}
});
// #enddocregion content
// #docregion view
var AppComponent = ng.core.Component({
selector: 'heroes-queries',
template:
'<hero *ngFor="#hero of heroData"' +
'[hero]="hero">' +
'<active-label></active-label>' +
'</hero>' +
'<button (click)="activate()">' +
'Activate' +
'</button>',
directives: [
HeroComponent,
ActiveLabelComponent
],
queries: {
heroCmps: new ng.core.ViewChildren(
HeroComponent)
}
}).Class({
constructor: function() {
this.heroData = [
{id: 1, name: 'Windstorm'},
{id: 2, name: 'Superman'}
];
},
activate: function() {
this.heroCmps.forEach(function(cmp) {
cmp.activate();
});
}
});
// #enddocregion view
app.HeroesQueriesComponent = AppComponent;
})(window.app = window.app || {});

View File

@ -0,0 +1,41 @@
// #docplaster
// #docregion appimport
(function(app) {
// #enddocregion appimport
// #docregion ng2import
var provide =
ng.core.provide;
var bootstrap =
ng.platform.browser.bootstrap;
var LocationStrategy =
ng.router.LocationStrategy;
var HashLocationStrategy =
ng.router.HashLocationStrategy;
// #enddocregion ng2import
// #docregion appimport
var HeroComponent = app.HeroComponent;
// #enddocregion appimport
document.addEventListener('DOMContentLoaded', function() {
bootstrap(HeroComponent);
bootstrap(app.HeroComponentDsl);
bootstrap(app.HeroLifecycleComponent);
bootstrap(app.HeroDIComponent, [app.DataService]);
bootstrap(app.HeroDIInlineComponent, [app.DataService]);
bootstrap(app.HeroDIInjectComponent, [
ng.core.provide('heroName', {useValue: 'Windstorm'})
]);
bootstrap(app.HeroDIInjectComponent2, [
ng.core.provide('heroName', {useValue: 'Bombasto'})
]);
bootstrap(app.HeroDIInjectAdditionalComponent);
bootstrap(app.HeroIOComponent);
bootstrap(app.HeroesHostBindingsComponent);
bootstrap(app.HeroesQueriesComponent);
});
// #docregion appimport
})(window.app = window.app || {});
// #enddocregion appimport

View File

@ -0,0 +1,62 @@
<!DOCTYPE html>
<html>
<head>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<!-- IE required polyfills, in this exact order -->
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
<script src="node_modules/systemjs/dist/system-polyfills.js"></script>
<script src="node_modules/angular2/es6/dev/src/testing/shims_for_IE.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/rxjs/bundles/Rx.umd.js"></script>
<script src="node_modules/angular2/bundles/angular2-all.umd.dev.js"></script>
<script src="app/data.service.js"></script>
<script src="app/hero.component.js"></script>
<script src="app/hero-dsl.component.js"></script>
<script src="app/hero-lifecycle.component.js"></script>
<script src="app/hero-io.component.js"></script>
<script src="app/hero-di.component.js"></script>
<script src="app/hero-di-inline.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/heroes-bindings.component.js"></script>
<script src="app/heroes-queries.component.js"></script>
<script src="app/main.js"></script>
</head>
<body>
<a id="toc"></a>
<h1>TypeScript to JavaScript</h1>
<a href="#class-metadata">Classes and Class Metadata</a><br>
<a href="#property-metadata">Input and Output Metadata</a><br>
<a href="#dependency-injection">Dependency Injection</a><br>
<a href="#other-property-metadata">Host and Query Metadata</a><br>
<hr>
<h4 id="class-metadata">Classes and Class Metadata</h4>
<hero-view>Loading hero-view...</hero-view>
<hero-view-2>Loading hero-view2...</hero-view-2>
<hero-lifecycle>Loading hero-lifecycle...</hero-lifecycle>
<hr>
<h4 id="property-metadata">Input and Output Metadata</h4>
<hero-io>Loading hero-io...</hero-io>
<hr>
<h4 id="dependency-injection">Dependency Injection</h4>
<hero-di>Loading hero-di...</hero-di>
<hero-di-inline>Loading hero-di-inline...</hero-di-inline>
<hero-di-inline2>Loading hero-di-inline2...</hero-di-inline2>
<hero-di-inject>Loading hero-di-inject...</hero-di-inject>
<hero-di-inject-additional>Loading hero-di-inject-additional...</hero-di-inject-additional>
<hr>
<h4 id="other-property-metadata">Host and Query Metadata</h4>
<heroes-bindings>Loading heroes-bindings...</heroes-bindings>
<heroes-queries>Loading heroes-queries...</heroes-queries>
</body>
</html>

View File

@ -0,0 +1,7 @@
{
"description": "TypeScript to JavaScript",
"files":[
"!**/karma*.*"
],
"tags":["cookbook"]
}

View File

@ -0,0 +1 @@
**/*.js

View File

@ -0,0 +1,10 @@
import {Injectable} from 'angular2/core';
@Injectable()
export class DataService {
constructor() {
}
getHeroName() {
return 'Windstorm';
}
}

View File

@ -0,0 +1,48 @@
import {
Attribute,
Component,
ElementRef,
Inject,
Optional,
Query,
QueryList
} from 'angular2/core';
// #docregion
@Component({
selector: 'hero-title',
template: `
<h1>{{titlePrefix}} {{title}}</h1>
<button (click)="ok()">OK</button>
<ng-content></ng-content>
`
})
export class TitleComponent {
constructor(
@Inject('titlePrefix')
@Optional()
private titlePrefix:string,
@Attribute('title')
private title:string,
@Query('okMsg')
private msg:QueryList<ElementRef>) {
}
ok() {
let msgEl =
this.msg.first.nativeElement;
msgEl.textContent = 'OK!';
}
}
// #enddocregion
@Component({
selector: 'hero-di-inject-additional',
template: `<hero-title title="Tour of Heroes">
<span #okMsg class="ok-msg"></span>
</hero-title>`,
directives: [TitleComponent]
})
export class AppComponent {
}

View File

@ -0,0 +1,14 @@
import {Component, Inject} from 'angular2/core';
// #docregion
@Component({
selector: 'hero-di-inject',
template: `<h1>Hero: {{name}}</h1>`
})
export class HeroComponent {
constructor(
@Inject('heroName')
private name:string) {
}
}
// #enddocregion

View File

@ -0,0 +1,15 @@
import {Component} from 'angular2/core';
import {DataService} from './data.service';
// #docregion
@Component({
selector: 'hero-di',
template: `<h1>Hero: {{name}}</h1>`
})
export class HeroComponent {
name:string;
constructor(dataService:DataService) {
this.name = dataService.getHeroName();
}
}
// #enddocregion

View File

@ -0,0 +1,56 @@
import {Component, EventEmitter, Input, Output} from 'angular2/core';
// #docregion
@Component({
selector: 'my-confirm',
template: `
<button (click)="onOkClick()">
{{okMsg}}
</button>
<button (click)="onNotOkClick()">
{{notOkMsg}}
</button>
`
})
export class ConfirmComponent {
@Input() okMsg:string;
@Input('cancelMsg') notOkMsg:string;
@Output() ok =
new EventEmitter();
@Output('cancel') notOk =
new EventEmitter();
onOkClick() {
this.ok.next(true);
}
onNotOkClick() {
this.notOk.next(true);
}
}
// #enddocregion
@Component({
selector: 'hero-io',
template: `
<my-confirm [okMsg]="'OK'"
[cancelMsg]="'Cancel'"
(ok)="onOk()"
(cancel)="onCancel()">
</my-confirm>
<span *ngIf="okClicked">OK clicked</span>
<span *ngIf="cancelClicked">Cancel clicked</span>
`,
directives: [ConfirmComponent]
})
export class AppComponent {
okClicked:boolean;
cancelClicked:boolean;
onOk() {
this.okClicked = true;
}
onCancel() {
this.cancelClicked = true;
}
}

View File

@ -0,0 +1,19 @@
// #docplaster
// #docregion
import {Component, OnInit}
from 'angular2/core';
// #enddocregion
@Component({
selector: 'hero-lifecycle',
template: `<h1>Hero: {{name}}</h1>`
})
// #docregion
export class HeroComponent
implements OnInit {
name:string;
ngOnInit() {
this.name = 'Windstorm';
}
}
// #enddocregion

View File

@ -0,0 +1,18 @@
// #docplaster
// #docregion metadata
import {Component} from 'angular2/core';
@Component({
selector: 'hero-view',
template:
'<h1>Hero: {{getName()}}</h1>'
})
// #docregion appexport
// #docregion class
export class HeroComponent {
title = 'Hero Detail';
getName() {return 'Windstorm';}
}
// #enddocregion class
// #enddocregion appexport
// #enddocregion metadata

View File

@ -0,0 +1,28 @@
import {Component, HostBinding, HostListener} from 'angular2/core';
// #docregion
@Component({
selector: 'heroes-bindings',
template: `<h1 [class.active]="active">
Tour of Heroes
</h1>`
})
export class HeroesComponent {
@HostBinding() title = 'Tooltip content';
@HostBinding('class.heading')
hClass = true
active:boolean;
constructor() {}
@HostListener('click')
clicked() {
this.active = !this.active;
}
@HostListener('dblclick', ['$event'])
doubleClicked(evt:Event) {
this.active = true;
}
}
// #enddocregion

View File

@ -0,0 +1,79 @@
import {
Component,
ViewChildren,
ContentChild,
QueryList,
Input
} from 'angular2/core';
@Component({
selector: 'active-label',
template: `<span class="active-label"
*ngIf="active">
Active
</span>`
})
class ActiveLabelComponent {
active:boolean;
activate() {
this.active = true;
}
}
// #docregion content
@Component({
selector: 'hero',
template: `<h2 [class.active]=active>
{{hero.name}}
<ng-content></ng-content>
</h2>`
})
class HeroComponent {
@Input() hero:any;
active:boolean;
@ContentChild(ActiveLabelComponent)
label:ActiveLabelComponent
activate() {
this.active = true;
this.label.activate();
}
}
// #enddocregion content
// #docregion view
@Component({
selector: 'heroes-queries',
template: `
<hero *ngFor="#hero of heroData"
[hero]="hero">
<active-label></active-label>
</hero>
<button (click)="activate()">
Activate
</button>
`,
directives: [
HeroComponent,
ActiveLabelComponent
]
})
export class HeroesQueriesComponent {
heroData = [
{id: 1, name: 'Windstorm'},
{id: 2, name: 'Superman'}
];
@ViewChildren(HeroComponent)
heroCmps:QueryList<HeroComponent>;
activate() {
this.heroCmps.forEach(
(cmp) => cmp.activate()
);
}
}
// #enddocregion view

View File

@ -0,0 +1,35 @@
// #docregion ng2import
import {provide}
from 'angular2/core';
import {bootstrap}
from 'angular2/platform/browser';
import {
LocationStrategy,
HashLocationStrategy
} from 'angular2/router';
// #enddocregion ng2import
// #docregion appimport
import {HeroComponent}
from './hero.component';
// #enddocregion appimport
import {HeroComponent as HeroLifecycleComponent} from './hero-lifecycle.component';
import {HeroComponent as HeroDIComponent} from './hero-di.component';
import {HeroComponent as HeroDIInjectComponent} from './hero-di-inject.component';
import {AppComponent as AppDIInjectAdditionalComponent} from './hero-di-inject-additional.component';
import {AppComponent as AppIOComponent} from './hero-io.component';
import {HeroesComponent as HeroesHostBindingsComponent} from './heroes-bindings.component';
import {HeroesQueriesComponent} from './heroes-queries.component';
import {DataService} from './data.service';
bootstrap(HeroComponent);
bootstrap(HeroLifecycleComponent);
bootstrap(HeroDIComponent, [DataService]);
bootstrap(HeroDIInjectComponent, [
provide('heroName', {useValue: 'Windstorm'})
]);
bootstrap(AppDIInjectAdditionalComponent);
bootstrap(AppIOComponent);
bootstrap(HeroesHostBindingsComponent);
bootstrap(HeroesQueriesComponent);

View File

@ -0,0 +1,61 @@
<!DOCTYPE html>
<html>
<head>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<!-- IE required polyfills, in this exact order -->
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
<script src="node_modules/systemjs/dist/system-polyfills.js"></script>
<script src="node_modules/angular2/es6/dev/src/testing/shims_for_IE.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<script src="node_modules/angular2/bundles/router.dev.js"></script>
<script>
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
}
}
});
System.import('app/main')
.then(null, console.error.bind(console));
</script>
</head>
<body>
<a id="toc"></a>
<h1>TypeScript to JavaScript</h1>
<a href="#class-metadata">Classes and Class Metadata</a><br>
<a href="#property-metadata">Input and Output Metadata</a><br>
<a href="#dependency-injection">Dependency Injection</a><br>
<a href="#other-property-metadata">Host and Query Metadata</a><br>
<hr>
<h4 id="class-metadata">Classes and Class Metadata</h4>
<hero-view id="class-metadata">Loading hero-view...</hero-view>
<hero-lifecycle>Loading hero-lifecycle...</hero-lifecycle>
<hr>
<h4 id="property-metadata">Input and Output Metadata</h4>
<hero-io>Loading hero-io...</hero-io>
<hr>
<h4 id="dependency-injection">Dependency Injection</h4>
<hero-di>Loading hero-di...</hero-di>
<hero-di-inject>Loading hero-di-inject...</hero-di-inject>
<hero-di-inject-additional>Loading hero-di-inject-additional...</hero-di-inject-additional>
<hr>
<h4 id="other-property-metadata">Host and Query Metadata</h4>
<heroes-bindings>Loading heroes-bindings...</heroes-bindings>
<heroes-queries id="other-property-metadata">Loading heroes-queries...</heroes-queries>
</body>
</html>

View File

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

View File

@ -2,18 +2,30 @@
"index": {
"title": "Cookbook",
"navTitle": "Overview",
"description": "A collection of recipes for common Angular application scenarios"
"intro": "A collection of recipes for common Angular application scenarios"
},
"a1-a2-quick-reference": {
"title": "Angular 1 to 2 Quick Reference",
"navTitle": "Angular 1 to 2 Quick Ref",
"description": "Learn how Angular 1 concepts and techniques map to Angular 2",
"intro": "Learn how Angular 1 concepts and techniques map to Angular 2",
"hide": true
},
"component-communication": {
"title": "Component Interaction",
"description": "Share information between different directives and components"
"intro": "Share information between different directives and components"
},
"dynamic-form": {
"title": "Dynamic Form",
"intro": "Render dynamic forms with NgFormModel",
"hide": true
},
"ts-to-js": {
"title": "TypeScript to JavaScript",
"intro": "Convert Angular 2 TypeScript examples into ES5 JavaScript",
"hide": true
}
}

View File

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

View File

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

View File

@ -2,17 +2,29 @@
"index": {
"title": "Cookbook",
"navTitle": "Overview",
"description": "A collection of recipes for common Angular application scenarios"
"intro": "A collection of recipes for common Angular application scenarios"
},
"a1-a2-quick-reference": {
"title": "Angular 1 to 2 Quick Reference",
"navTitle": "Angular 1 to 2 Quick Ref",
"description": "Learn how Angular 1 concepts and techniques map to Angular 2"
"intro": "Learn how Angular 1 concepts and techniques map to Angular 2"
},
"component-communication": {
"title": "Component Interaction",
"description": "Share information between different directives and components"
"intro": "Share information between different directives and components"
},
"dynamic-form": {
"title": "Dynamic Form",
"intro": "Render dynamic forms with NgFormModel",
"hide": true
},
"ts-to-js": {
"title": "TypeScript to JavaScript",
"intro": "Convert Angular 2 TypeScript examples into ES5 JavaScript"
}
}

View File

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

View File

@ -1 +1,3 @@
!= partial("../../../_includes/_ts-temp")
include ../../../../_includes/_util-fns
+includeShared('../../../ts/latest/cookbook/index.jade', 'cookbook')

View File

@ -0,0 +1,471 @@
include ../../../../_includes/_util-fns
:marked
Everything that we can do in Angular 2 in TypeScript, we can also do
in JavaScript. Translating from one language to the other is mostly a
matter of changing the way we organize our code and the way we access
Angular 2 APIs.
Since TypeScript is a popular language option in Angular 2, many of the
code examples you see on the Internet as well as on this site are written
in TypeScript. This cookbook contains recipes for translating these kinds of
code examples to ES5, so that they can be applied to Angular 2 JavaScript
applications.
<a id="toc"></a>
:marked
## Table of contents
[Modularity: imports and exports](#modularity)
[Classes and Class Metadata](#class-metadata)
[Input and Output Metadata](#property-metadata)
[Dependency Injection](#dependency-injection)
[Host and Query Metadata](#other-property-metadata)
**Run and compare the live [TypeScript](/resources/live-examples/cb-ts-to-js/ts/plnkr.html) and
[JavaScript](/resources/live-examples/cb-ts-to-js/js/plnkr.html) code shown in this cookbook.**
a(id="modularity")
.l-main-section
:marked
## Importing and Exporting
- var top="vertical-align:top"
table(width="100%")
col(width="50%")
col(width="50%")
tr
th TypeScript
th ES5 JavaScript
tr(style=top)
td
:marked
### Importing Angular 2 Code
In TypeScript code, Angular 2 classes, functions, and other members
are imported with TypeScript `import` statements:
+makeExample('cb-ts-to-js/ts/app/main.ts', 'ng2import')(format="." )
td
:marked
### Accessing Angular 2 Code through the ng global
In JavaScript code, when using
[the Angular 2 UMD bundles](https://github.com/angular/angular/blob/master/modules/angular2/docs/bundles/overview.md),
we can access Angular code through the global `ng` object. In the
nested members of this object we'll find everything we would import
from `angular2` in TypeScript:
+makeExample('cb-ts-to-js/js/app/main.js', 'ng2import')(format="." )
tr(style=top)
td
:marked
### Importing and Exporting Application Code
Each file in an Angular 2 TypeScript application constitutes a
TypeScript module. When we want to make something from a module available
to other modules, we `export` it.
+makeExample('cb-ts-to-js/ts/app/hero.component.ts', 'appexport')(format="." )
:marked
In other modules we can then `import` things that have been exported
elsewhere.
+makeExample('cb-ts-to-js/ts/app/main.ts', 'appimport')(format="." )
td
:marked
### Sharing Application Code
In an Angular 2 JavaScript application, we load each file to the page
using a `<script>` tag. Each file can make things available to other
files via the shared global `window` scope.
We often introduce an application namespace
object (such as `"app"`) onto `window` and attach everything we need
to share to that namespace object.
We also wrap our code in an
[Immediately Invoked Function Expression (IIFE)](https://en.wikipedia.org/wiki/Immediately-invoked_function_expression).
These practices together prevent our code from
polluting the global scope.
+makeExample('cb-ts-to-js/js/app/hero.component.js', 'appexport')(format="." )
:marked
We can then access anything from this shared namespace in
other files.
+makeExample('cb-ts-to-js/js/app/main.js', 'appimport')(format="." )
:marked
Note that the order of `<script>` tags on the page is significant.
We must load a file that defines a shared member before
a file that uses that member.
.alert.is-helpful
:marked
Alternatively, we can use a module loader such as Webpack or
Browserify in an Angular 2 JavaScript project. In such a project, we would
use CommonJS modules and the `require` function to load Angular 2 framework code.
We would then use `module.exports` and `require` to export and import application
code.
a(id="class-metadata")
.l-main-section
:marked
## Classes and Class Metadata
- var top="vertical-align:top"
table(width="100%")
col(width="50%")
col(width="50%")
tr
th TypeScript
th ES5 JavaScript
tr(style=top)
td
:marked
### Classes
We put most of our Angular 2 TypeScript code into TypeScript classes.
+makeExample('cb-ts-to-js/ts/app/hero.component.ts', 'class')(format="." )
td
:marked
### Constructors and Prototypes
ES5 JavaScript has no classes. We use the constructor
pattern instead which works with Angular 2 as well as classes do.
+makeExample('cb-ts-to-js/js/app/hero.component.js', 'constructorproto')(format="." )
tr(style=top)
td
:marked
### Metadata with Decorators
Most Angular 2 classes have one or more TypeScript *decorators*
attached to provide configuration and metadata. For example,
a component must have a [`@Component`](../api/core/Component-decorator.html) decorator.
+makeExample('cb-ts-to-js/ts/app/hero.component.ts', 'metadata')(format="." )
td
:marked
### Metadata with the Annotations Array
In JavaScript, we can attach an `annotations` array to a constructor
to provide metadata.
Each item in the array corresponds to a TypeScript decorator.
In the following example, we create a new instance of `Component` that corresponds
to the [`@Component`](../api/core/Component-decorator.html) TypeScript decorator.
+makeExample('cb-ts-to-js/js/app/hero.component.js', 'metadata')(format="." )
:marked
### Metadata with The Class Convenience API
The pattern of creating a constructor and decorating it with metadata
is so common that Angular provides an alternative convenience API for it.
This API lets us define everything in a single expression.
With this API we first call the `ng.core.Component` function,
followed by a chained `Class` method call. The argument to `Class`
is an object that defines the constructor and the instance methods
of the component:
+makeExample('cb-ts-to-js/js/app/hero-dsl.component.js', 'component')(format="." )
:marked
Similar APIs are also available for other decorators. You can define a
directive:
code-example.
var MyDirective = ng.core.Directive({
...
}).Class({
...
});
:marked
Or a pipe:
code-example.
var MyPipe = ng.core.Pipe({
name: 'myPipe'
}).Class({
...
});
tr(style=top)
td
:marked
### Interfaces
When defining classes that need to implement a certain method, it
is common to use TypeScript interfaces that enforce that the
method signature is correct. Component lifecycle methods like `ngOnInit`
are one example of this pattern. `ngOnInit` is defined in the `OnInit`
interface.
+makeExample('cb-ts-to-js/ts/app/hero-lifecycle.component.ts')(format="." )
td
:marked
### Implementing Methods without Interfaces
TypeScript interfaces are purely for developer convenience
and are not used by Angular 2 at runtime. This means that in JavaScript
code we don't need to substitute anything for interfaces. We can just
implement the methods.
+makeExample('cb-ts-to-js/js/app/hero-lifecycle.component.js')(format="." )
a(id="property-metadata")
.l-main-section
:marked
## Input and Output Metadata
- var top="vertical-align:top"
table(width="100%")
col(width="50%")
col(width="50%")
tr
th TypeScript
th ES5 JavaScript
tr(style=top)
td
:marked
### Input and Output Decorators
In TypeScript, property decorators are often used to provide additional metadata
for components and directives.
For [inputs and outputs](../guide/template-syntax.html#inputs-outputs),
we use [`@Input`](../api/core/Input-var.html)
and [`@Output`](../api/core/Output-var.html) property decorators.
They may optionally specify input and output binding names if we want them to be
different from the class property names.
+makeExample('cb-ts-to-js/ts/app/hero-io.component.ts')(format="." )
.alert.is-helpful
:marked
In TypeScript we can also use the `inputs` and `outputs` array metadata
instead of the `@Input` and `@Output` property decorators.
td
:marked
### Inputs and Outputs in Component Metadata
There is no equivalent of a property decorator in ES5 JavaScript. Instead,
we add comparable information to the `Component` (or `Directive`) metadata.
In this example, we add `inputs` and `outputs` array attributes
containing the input and output property names.
If we need a binding name that is different from the
property itself, we use the `propertyName: bindingName` syntax.
+makeExample('cb-ts-to-js/js/app/hero-io.component.js')(format="." )
.l-main-section
:marked
## Dependency Injection
- var top="vertical-align:top"
table(width="100%")
col(width="50%")
col(width="50%")
tr
th TypeScript
th ES5 JavaScript
tr(style=top)
td
:marked
### Injection by Type
Angular 2 can often use TypeScript type information to
determine what needs to be injected.
+makeExample('cb-ts-to-js/ts/app/hero-di.component.ts')(format="." )
td
:marked
### Injection with Parameter Tokens
Since no type information is available in ES5 JavaScript,
we must identify "injectables" in some other way.
We attach a `parameters` array to the constructor function.
Each array item is the dependency injection token that identifies the thing to be injected.
Often the token is the constructor function for the class-like dependency.
+makeExample('cb-ts-to-js/js/app/hero-di.component.js')(format="." )
:marked
When using the class convenience API, we can also supply the parameter
tokens by wrapping the constructor in an array.
+makeExample('cb-ts-to-js/js/app/hero-di-inline.component.js')(format="." )
tr(style=top)
td
:marked
### Injection with the @Inject decorator
When the thing being injected doesn't correspond directly to a type,
we use the `@Inject()` decorator to supply the injection token.
In this example, we're injecting a string identified by the "heroName" token.
+makeExample('cb-ts-to-js/ts/app/hero-di-inject.component.ts')(format="." )
td
:marked
### Injection with plain string tokens
In JavaScript we add the token string to the injection parameters array.
+makeExample('cb-ts-to-js/js/app/hero-di-inject.component.js','parameters')(format="." )
:marked
Alternatively, we can create a token with the `Inject` method and
add that to the constructor array in the annotations like this:
+makeExample('cb-ts-to-js/js/app/hero-di-inject.component.js','ctor')(format="." )
tr(style=top)
td
:marked
### Additional Injection Decorators
We can attach additional decorators to constructor parameters
to qualify the injection behavior. We can mark
optional dependencies with the [`@Optional`](../api/core/Optional-var.html),
inject host element attributes with [`@Attribute`](../api/core/Attribute-var.html),
inject content child queries with [`@Query`](../api/core/Query-var.html)
and inject view child queries with [`@ViewQuery`](../api/core/ViewQuery-var.html)).
+makeExample('cb-ts-to-js/ts/app/hero-di-inject-additional.component.ts')(format="." )
td
:marked
### Additional Injection Metadata with Nested Arrays
To achieve the same effect in JavaScript, use the constructor array notation
in which the injection information precedes the constructor function itself.
Use the injection support functions `Attribute`, `Host`, `Optional`, `Self`, `SkipSelf`,
`Query` and `ViewQuery` to qualify dependency injection behavior.
Use a nested array to combine injection functions.
+makeExample('cb-ts-to-js/js/app/hero-di-inject-additional.component.js')(format="." )
:marked
We can apply other additional parameter decorators such as
[`@Host`](../api/core/Host-var.html) and
[`@SkipSelf`](../api/core/SkipSelf-var.html) in the same way -
by adding `new ng.core.Host()` or `ng.core.SkipSelf()` in the
parameters array.
a(id="other-property-metadata")
.l-main-section
:marked
## Host and Query Metadata
- var top="vertical-align:top"
table(width="100%")
col(width="50%")
col(width="50%")
tr
th TypeScript
th ES5 JavaScript
tr(style=top)
td
:marked
### Host Decorators
We can use host property decorators to bind a host element to a component or directive.
The [`@HostBinding`](../api/core/HostBinding-var.html) decorator
binds host element properties to component data properties.
The [`@HostListener`](../api/core/HostListener-var.html) decorator bimds
host element events to component event handlers.
+makeExample('cb-ts-to-js/ts/app/heroes-bindings.component.ts')(format="." )
.alert.is-helpful
:marked
In TypeScript we can also use `host` metadata
instead of the `@HostBinding` and `@HostListener` property decorators.
td
:marked
### Host Metadata
We add a `host` attribute to the component metadata to achieve the
same effect as `@HostBinding` and `@HostListener`.
The `host` value is an object whose properties are host property and listener bindings:
* Each key follows regular Angular 2 binding syntax: `[property]` for host bindings
or `(event)` for host listeners.
* Each value identifies the corresponding component property or method.
+makeExample('cb-ts-to-js/js/app/heroes-bindings.component.js')(format="." )
tr(style=top)
td
:marked
### Query Decorators
There are several property decorators for querying the descendants of
a component or directive.
The [`@ViewChild`](../api/core/ViewChild-var.html) and
[`@ViewChildren`](../api/core/ViewChildren-var.html) property decorators
allow a component to query instances of other components that are used in
its view.
+makeExample('cb-ts-to-js/ts/app/heroes-queries.component.ts', 'view')(format="." )
:marked
The [`@ContentChild`](../api/core/ContentChild-var.html) and
[`@ContentChildren`](../api/core/ContentChildren-var.html) property decorators
allow a component to query instances of other components that have been projected
into its view from elsewhere.
+makeExample('cb-ts-to-js/ts/app/heroes-queries.component.ts', 'content')(format="." )
.alert.is-helpful
:marked
In TypeScript we can also use the `queries` metadata
instead of the `@ViewChild` and `@ContentChild` property decorators.
td
:marked
### Query Metadata
We access a component's view children by adding a `queries` attribute to
the component metadata. It should be an object where:
* Each key is the name of a component property that will hold the view children
* Each value is an instance of either `ViewChild` or `ViewChildren`.
+makeExample('cb-ts-to-js/js/app/heroes-queries.component.js', 'view')(format="." )
:marked
We add *content* child queries to the same `queries` attribute
in the same manner, using instances of `ContentChild` or `ContentChildren`:
+makeExample('cb-ts-to-js/js/app/heroes-queries.component.js', 'content')(format="." )

View File

@ -8,16 +8,21 @@
"a1-a2-quick-reference": {
"title": "Angular 1 to 2 Quick Reference",
"navTitle": "Angular 1 to 2 Quick Ref",
"description": "Learn how Angular 1 concepts and techniques map to Angular 2"
"intro": "Learn how Angular 1 concepts and techniques map to Angular 2"
},
"component-communication": {
"title": "Component Interaction",
"description": "Share information between different directives and components"
"intro": "Share information between different directives and components"
},
"dynamic-form": {
"title": "Dynamic Form",
"description": "Render dynamic forms with NgFormModel"
"intro": "Render dynamic forms with NgFormModel"
},
"ts-to-js": {
"title": "TypeScript to JavaScript",
"intro": "Convert Angular 2 TypeScript examples into ES5 JavaScrip"
}
}

View File

@ -1,5 +1,6 @@
include ../_util-fns
// #docregion cookbook
:marked
# Angular 2 Cookbook
@ -27,3 +28,4 @@ include ../_util-fns
[angular.io](https://github.com/angular/angular.io) github repository.
Post issues with *Angular 2 itself* to the [angular](https://github.com/angular/angular) github repository.
// #enddocregion cookbook

View File

@ -0,0 +1 @@
!= partial("../../../js/latest/cookbook/ts-to-js")

View File

@ -42,6 +42,11 @@ var _rxData = [
from: 'node_modules/angular2/bundles/angular2.dev.js',
to: 'https://code.angularjs.org/2.0.0-beta.12/angular2.dev.js'
},
{
pattern: 'script',
from: 'node_modules/angular2/bundles/angular2-all.umd.dev.js',
to: 'https://code.angularjs.org/2.0.0-beta.12/angular2-all.umd.dev.js'
},
{
pattern: 'script',
from: 'node_modules/angular2/bundles/angular2-all.umd.js',