diff --git a/public/docs/_examples/forms/js/app/main.js b/public/docs/_examples/forms/js/app/main.js
index 5930bdd061..2b866b3685 100644
--- a/public/docs/_examples/forms/js/app/main.js
+++ b/public/docs/_examples/forms/js/app/main.js
@@ -1,6 +1,9 @@
// #docregion
(function(app) {
document.addEventListener('DOMContentLoaded', function() {
- ng.platformBrowserDynamic.bootstrap(app.AppComponent);
+ ng.platformBrowserDynamic.bootstrap(app.AppComponent,[
+ ng.forms.disableDeprecatedForms(),
+ ng.forms.provideForms()
+ ]);
});
})(window.app || (window.app = {}));
diff --git a/public/docs/_examples/forms/js/index.html b/public/docs/_examples/forms/js/index.html
index e15f41e4fe..8353dfb180 100644
--- a/public/docs/_examples/forms/js/index.html
+++ b/public/docs/_examples/forms/js/index.html
@@ -24,6 +24,7 @@
+
diff --git a/public/docs/_examples/forms/ts/app/hero-form.component.html b/public/docs/_examples/forms/ts/app/hero-form.component.html
index 6fcfc2a7cb..80401a1313 100644
--- a/public/docs/_examples/forms/ts/app/hero-form.component.html
+++ b/public/docs/_examples/forms/ts/app/hero-form.component.html
@@ -13,7 +13,7 @@
diff --git a/public/docs/_examples/forms/ts/app/main.ts b/public/docs/_examples/forms/ts/app/main.ts
index 5338161d66..9e0244c26a 100644
--- a/public/docs/_examples/forms/ts/app/main.ts
+++ b/public/docs/_examples/forms/ts/app/main.ts
@@ -1,6 +1,11 @@
// #docregion
import { bootstrap } from '@angular/platform-browser-dynamic';
+import { disableDeprecatedForms, provideForms } from '@angular/forms';
import { AppComponent } from './app.component';
-bootstrap(AppComponent);
+bootstrap(AppComponent, [
+ disableDeprecatedForms(),
+ provideForms()
+ ])
+ .catch((err: any) => console.error(err));
diff --git a/public/docs/_examples/package.json b/public/docs/_examples/package.json
index 1cb56e65de..4f4dd51f00 100644
--- a/public/docs/_examples/package.json
+++ b/public/docs/_examples/package.json
@@ -25,15 +25,16 @@
"author": "",
"license": "ISC",
"dependencies": {
- "@angular/common": "2.0.0-rc.2",
- "@angular/compiler": "2.0.0-rc.2",
- "@angular/core": "2.0.0-rc.2",
- "@angular/http": "2.0.0-rc.2",
- "@angular/platform-browser": "2.0.0-rc.2",
- "@angular/platform-browser-dynamic": "2.0.0-rc.2",
- "@angular/router": "2.0.0-rc.2",
+ "@angular/common": "2.0.0-rc.3",
+ "@angular/compiler": "2.0.0-rc.3",
+ "@angular/core": "2.0.0-rc.3",
+ "@angular/forms": "0.1.1",
+ "@angular/http": "2.0.0-rc.3",
+ "@angular/platform-browser": "2.0.0-rc.3",
+ "@angular/platform-browser-dynamic": "2.0.0-rc.3",
+ "@angular/router": "3.0.0-alpha.7",
"@angular/router-deprecated": "2.0.0-rc.2",
- "@angular/upgrade": "2.0.0-rc.2",
+ "@angular/upgrade": "2.0.0-rc.3",
"angular2-in-memory-web-api": "0.0.12",
"bootstrap": "^3.3.6",
"core-js": "^2.4.0",
diff --git a/public/docs/_examples/quickstart/js/package.1.json b/public/docs/_examples/quickstart/js/package.1.json
index c54e43307a..b5b25a4f10 100644
--- a/public/docs/_examples/quickstart/js/package.1.json
+++ b/public/docs/_examples/quickstart/js/package.1.json
@@ -7,15 +7,16 @@
},
"license": "ISC",
"dependencies": {
- "@angular/common": "2.0.0-rc.2",
- "@angular/compiler": "2.0.0-rc.2",
- "@angular/core": "2.0.0-rc.2",
- "@angular/http": "2.0.0-rc.2",
- "@angular/platform-browser": "2.0.0-rc.2",
- "@angular/platform-browser-dynamic": "2.0.0-rc.2",
- "@angular/router": "2.0.0-rc.2",
+ "@angular/common": "2.0.0-rc.3",
+ "@angular/compiler": "2.0.0-rc.3",
+ "@angular/core": "2.0.0-rc.3",
+ "@angular/forms": "0.1.1",
+ "@angular/http": "2.0.0-rc.3",
+ "@angular/platform-browser": "2.0.0-rc.3",
+ "@angular/platform-browser-dynamic": "2.0.0-rc.3",
+ "@angular/router": "3.0.0-alpha.7",
"@angular/router-deprecated": "2.0.0-rc.2",
- "@angular/upgrade": "2.0.0-rc.2",
+ "@angular/upgrade": "2.0.0-rc.3",
"core-js": "^2.4.0",
"reflect-metadata": "0.1.3",
diff --git a/public/docs/_examples/quickstart/js/styles.1.css b/public/docs/_examples/quickstart/js/styles.1.css
new file mode 100644
index 0000000000..fbc30e2c9e
--- /dev/null
+++ b/public/docs/_examples/quickstart/js/styles.1.css
@@ -0,0 +1,14 @@
+/* #docregion */
+h1 {
+ color: #369;
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 250%;
+}
+body {
+ margin: 2em;
+}
+
+ /*
+ * See https://github.com/angular/angular.io/blob/master/public/docs/_examples/styles.css
+ * for the full set of master styles used by the documentation samples
+ */
diff --git a/public/docs/_examples/quickstart/ts/package.1.json b/public/docs/_examples/quickstart/ts/package.1.json
index afd11cdda5..9353dfaa3e 100644
--- a/public/docs/_examples/quickstart/ts/package.1.json
+++ b/public/docs/_examples/quickstart/ts/package.1.json
@@ -11,15 +11,16 @@
},
"license": "ISC",
"dependencies": {
- "@angular/common": "2.0.0-rc.2",
- "@angular/compiler": "2.0.0-rc.2",
- "@angular/core": "2.0.0-rc.2",
- "@angular/http": "2.0.0-rc.2",
- "@angular/platform-browser": "2.0.0-rc.2",
- "@angular/platform-browser-dynamic": "2.0.0-rc.2",
- "@angular/router": "2.0.0-rc.2",
+ "@angular/common": "2.0.0-rc.3",
+ "@angular/compiler": "2.0.0-rc.3",
+ "@angular/core": "2.0.0-rc.3",
+ "@angular/forms": "0.1.1",
+ "@angular/http": "2.0.0-rc.3",
+ "@angular/platform-browser": "2.0.0-rc.3",
+ "@angular/platform-browser-dynamic": "2.0.0-rc.3",
+ "@angular/router": "3.0.0-alpha.7",
"@angular/router-deprecated": "2.0.0-rc.2",
- "@angular/upgrade": "2.0.0-rc.2",
+ "@angular/upgrade": "2.0.0-rc.3",
"systemjs": "0.19.27",
"core-js": "^2.4.0",
diff --git a/public/docs/_examples/quickstart/ts/systemjs.config.1.js b/public/docs/_examples/quickstart/ts/systemjs.config.1.js
index 4dbe1974c2..901b8f01d0 100644
--- a/public/docs/_examples/quickstart/ts/systemjs.config.1.js
+++ b/public/docs/_examples/quickstart/ts/systemjs.config.1.js
@@ -25,6 +25,7 @@
'common',
'compiler',
'core',
+ 'forms',
'http',
'platform-browser',
'platform-browser-dynamic',
diff --git a/public/docs/_examples/quickstart/ts/typings.1.json b/public/docs/_examples/quickstart/ts/typings.1.json
index 9125b50685..3385926d1f 100644
--- a/public/docs/_examples/quickstart/ts/typings.1.json
+++ b/public/docs/_examples/quickstart/ts/typings.1.json
@@ -1,7 +1,7 @@
{
"globalDependencies": {
- "core-js": "registry:dt/core-js#0.0.0+20160317120654",
- "jasmine": "registry:dt/jasmine#2.2.0+20160505161446",
- "node": "registry:dt/node#6.0.0+20160613154055"
+ "core-js": "registry:dt/core-js#0.0.0+20160602141332",
+ "jasmine": "registry:dt/jasmine#2.2.0+20160621224255",
+ "node": "registry:dt/node#6.0.0+20160621231320"
}
}
diff --git a/public/docs/_examples/router/e2e-spec.ts b/public/docs/_examples/router/e2e-spec.ts
index f86293e823..ac3e36f4ea 100644
--- a/public/docs/_examples/router/e2e-spec.ts
+++ b/public/docs/_examples/router/e2e-spec.ts
@@ -24,14 +24,18 @@ describe('Router', function () {
heroDetail: element(by.css('my-app > undefined > div')),
heroDetailTitle: element(by.css('my-app > undefined > div > h3')),
+ adminHref: hrefEles.get(2),
+ loginHref: hrefEles.get(3)
};
}
it('should be able to see the start screen', function () {
let page = getPageStruct();
- expect(page.hrefs.count()).toEqual(2, 'should be two dashboard choices');
+ expect(page.hrefs.count()).toEqual(4, 'should be two dashboard choices');
expect(page.crisisHref.getText()).toEqual('Crisis Center');
expect(page.heroesHref.getText()).toEqual('Heroes');
+ expect(page.adminHref.getText()).toEqual('Crisis Admin');
+ expect(page.loginHref.getText()).toEqual('Login');
});
it('should be able to see crises center items', function () {
diff --git a/public/docs/_examples/router/ts/app/app.component.2.ts b/public/docs/_examples/router/ts/app/app.component.2.ts
index 779d43a063..f1580cf6f8 100644
--- a/public/docs/_examples/router/ts/app/app.component.2.ts
+++ b/public/docs/_examples/router/ts/app/app.component.2.ts
@@ -3,7 +3,7 @@
// #docregion
import { Component } from '@angular/core';
-import { Router, ROUTER_DIRECTIVES } from '@angular/router';
+import { ROUTER_DIRECTIVES } from '@angular/router';
// #enddocregion
/*
diff --git a/public/docs/_examples/router/ts/app/app.routes.1.ts b/public/docs/_examples/router/ts/app/app.routes.1.ts
index 480b4eb95c..8c4caeda55 100644
--- a/public/docs/_examples/router/ts/app/app.routes.1.ts
+++ b/public/docs/_examples/router/ts/app/app.routes.1.ts
@@ -16,11 +16,11 @@ import { HeroDetailComponent } from './heroes/hero-detail.component';
// #docregion route-config
export const routes: RouterConfig = [
// #docregion route-defs
- { path: '/crisis-center', component: CrisisCenterComponent },
- { path: '/heroes', component: HeroListComponent },
+ { path: 'crisis-center', component: CrisisCenterComponent },
+ { path: 'heroes', component: HeroListComponent },
// #enddocregion route-defs
// #docregion hero-detail-route
- { path: '/hero/:id', component: HeroDetailComponent }
+ { path: 'hero/:id', component: HeroDetailComponent }
// #enddocregion hero-detail-route
];
diff --git a/public/docs/_examples/router/ts/app/app.routes.2.ts b/public/docs/_examples/router/ts/app/app.routes.2.ts
index 57c069c81d..d0d64e2ea0 100644
--- a/public/docs/_examples/router/ts/app/app.routes.2.ts
+++ b/public/docs/_examples/router/ts/app/app.routes.2.ts
@@ -9,8 +9,8 @@ import { HeroListComponent } from './hero-list.component';
// #docregion route-config
export const routes: RouterConfig = [
- { path: '/crisis-center', component: CrisisListComponent },
- { path: '/heroes', component: HeroListComponent }
+ { path: 'crisis-center', component: CrisisListComponent },
+ { path: 'heroes', component: HeroListComponent }
];
export const APP_ROUTER_PROVIDERS = [
diff --git a/public/docs/_examples/router/ts/app/app.routes.3.ts b/public/docs/_examples/router/ts/app/app.routes.3.ts
index f7becf70ea..c6e9cffc72 100644
--- a/public/docs/_examples/router/ts/app/app.routes.3.ts
+++ b/public/docs/_examples/router/ts/app/app.routes.3.ts
@@ -7,7 +7,7 @@ import { HeroesRoutes } from './heroes/heroes.routes';
export const routes = [
...HeroesRoutes,
- { path: '/crisis-center', component: CrisisListComponent }
+ { path: 'crisis-center', component: CrisisListComponent }
];
export const APP_ROUTER_PROVIDERS = [
diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.1.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.1.ts
index 0bd846c600..0c50d6b7df 100644
--- a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.1.ts
+++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.1.ts
@@ -7,11 +7,11 @@ import { CrisisCenterComponent } from './crisis-center.component';
// #docregion routes
export const CrisisCenterRoutes: RouterConfig = [
{
- path: '/crisis-center',
+ path: 'crisis-center',
component: CrisisCenterComponent,
children: [
- { path: '/', component: CrisisListComponent },
- { path: '/:id', component: CrisisDetailComponent }
+ { path: ':id', component: CrisisDetailComponent },
+ { path: '', component: CrisisListComponent }
]
}
];
diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.2.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.2.ts
index 2856544136..43eadbd55e 100644
--- a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.2.ts
+++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.2.ts
@@ -6,17 +6,20 @@ import { CrisisCenterComponent } from './crisis-center.component';
// #docregion routes
export const CrisisCenterRoutes: RouterConfig = [
+ // #docregion redirect
{
- path: '/crisis-center',
+ path: '',
+ redirectTo: '/crisis-center',
+ terminal: true
+ },
+ // #enddocregion redirect
+ {
+ path: 'crisis-center',
component: CrisisCenterComponent,
- index: true,
children: [
- { path: '/:id', component: CrisisDetailComponent },
- { path: '/', component: CrisisListComponent,
- index: true
- }
+ { path: ':id', component: CrisisDetailComponent },
+ { path: '', component: CrisisListComponent }
]
}
];
// #enddocregion routes
-
diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.3.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.3.ts
index 5d7170d500..1f194ac6f7 100644
--- a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.3.ts
+++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.3.ts
@@ -10,25 +10,28 @@ import { CanDeactivateGuard } from '../interfaces';
export const CrisisCenterRoutes: RouterConfig = [
{
- path: '/crisis-center',
+ path: '',
+ redirectTo: '/crisis-center',
+ terminal: true
+ },
+ {
+ path: 'crisis-center',
component: CrisisCenterComponent,
- index: true,
children: [
// #docregion admin-route-no-guard
{
- path: '/admin',
+ path: 'admin',
component: CrisisAdminComponent
},
// #enddocregion admin-route-no-guard
{
- path: '/:id',
+ path: ':id',
component: CrisisDetailComponent,
canDeactivate: [CanDeactivateGuard]
},
{
- path: '/',
- component: CrisisListComponent,
- index: true
+ path: '',
+ component: CrisisListComponent
}
]
}
@@ -40,7 +43,7 @@ export const CrisisCenterRoutes: RouterConfig = [
import { AuthGuard } from '../auth.guard';
{
- path: '/admin',
+ path: 'admin',
component: CrisisAdminComponent,
canActivate: [AuthGuard]
}
diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.4.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.4.ts
index 0e16b01d1c..f238e132a7 100644
--- a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.4.ts
+++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.4.ts
@@ -11,25 +11,28 @@ import { AuthGuard } from '../auth.guard';
export const CrisisCenterRoutes: RouterConfig = [
{
- path: '/crisis-center',
+ path: '',
+ redirectTo: '/crisis-center',
+ terminal: true
+ },
+ {
+ path: 'crisis-center',
component: CrisisCenterComponent,
- index: true,
children: [
{
- path: '/admin',
+ path: 'admin',
component: CrisisAdminComponent,
canActivate: [AuthGuard]
},
{
- path: '/:id',
+ path: ':id',
component: CrisisDetailComponent,
canDeactivate: [CanDeactivateGuard]
},
// #docregion default-route
{
- path: '/',
- component: CrisisListComponent,
- index: true
+ path: '',
+ component: CrisisListComponent
}
// #enddocregion default-route
]
@@ -42,7 +45,7 @@ export const CrisisCenterRoutes: RouterConfig = [
import { AuthGuard } from '../auth.guard';
{
- path: '/admin',
+ path: 'admin',
component: CrisisAdminComponent,
canActivate: [AuthGuard]
}
diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.ts
index f9d0a26f8d..a924aa5771 100644
--- a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.ts
+++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.ts
@@ -10,25 +10,29 @@ import { AuthGuard } from '../auth.guard';
export const CrisisCenterRoutes: RouterConfig = [
{
- path: '/crisis-center',
+ path: '',
+ redirectTo: '/crisis-center',
+ terminal: true
+ },
+ {
+ path: 'crisis-center',
component: CrisisCenterComponent,
- index: true,
children: [
// #docregion admin-route
{
- path: '/admin',
+ path: 'admin',
component: CrisisAdminComponent,
canActivate: [AuthGuard]
},
// #enddocregion admin-route
{
- path: '/:id',
+ path: ':id',
component: CrisisDetailComponent,
canDeactivate: [CanDeactivateGuard]
},
- { path: '/',
- component: CrisisListComponent,
- index: true
+ {
+ path: '',
+ component: CrisisListComponent
}
]
}
diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis.service.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis.service.ts
index 72e2500669..20597752cc 100644
--- a/public/docs/_examples/router/ts/app/crisis-center/crisis.service.ts
+++ b/public/docs/_examples/router/ts/app/crisis-center/crisis.service.ts
@@ -14,7 +14,7 @@ const CRISES = [
let crisesPromise = Promise.resolve(CRISES);
// #docregion
-import {Injectable} from '@angular/core';
+import { Injectable } from '@angular/core';
@Injectable()
export class CrisisService {
diff --git a/public/docs/_examples/router/ts/app/heroes/hero-detail.component.ts b/public/docs/_examples/router/ts/app/heroes/hero-detail.component.ts
index 9f765d0ad0..bfa9ca6d0f 100644
--- a/public/docs/_examples/router/ts/app/heroes/hero-detail.component.ts
+++ b/public/docs/_examples/router/ts/app/heroes/hero-detail.component.ts
@@ -56,7 +56,7 @@ export class HeroDetailComponent implements OnInit, OnDestroy {
let heroId = this.hero ? this.hero.id : null;
// Pass along the hero id if available
// so that the HeroList component can select that hero.
- this.router.navigate(['/heroes'], { queryParams: { id: `${heroId}`, foo: 'foo' } });
+ this.router.navigate(['/heroes'], { queryParams: { id: heroId, foo: 'foo' } });
}
// #enddocregion gotoHeroes-navigate
}
diff --git a/public/docs/_examples/router/ts/app/heroes/heroes.routes.ts b/public/docs/_examples/router/ts/app/heroes/heroes.routes.ts
index c4463cc8a1..80018d1f26 100644
--- a/public/docs/_examples/router/ts/app/heroes/heroes.routes.ts
+++ b/public/docs/_examples/router/ts/app/heroes/heroes.routes.ts
@@ -4,9 +4,9 @@ import { HeroListComponent } from './hero-list.component';
import { HeroDetailComponent } from './hero-detail.component';
export const HeroesRoutes: RouterConfig = [
- { path: '/heroes', component: HeroListComponent },
+ { path: 'heroes', component: HeroListComponent },
// #docregion hero-detail-route
- { path: '/hero/:id', component: HeroDetailComponent }
+ { path: 'hero/:id', component: HeroDetailComponent }
// #enddocregion hero-detail-route
];
// #enddocregion
diff --git a/public/docs/_examples/router/ts/app/login.component.ts b/public/docs/_examples/router/ts/app/login.component.ts
index 898ca2d4c5..2790bfc79d 100755
--- a/public/docs/_examples/router/ts/app/login.component.ts
+++ b/public/docs/_examples/router/ts/app/login.component.ts
@@ -4,7 +4,6 @@ import { Router } from '@angular/router';
import { AuthService } from './auth.service';
@Component({
- selector: 'login',
template: `
LOGIN
{{message}}
@@ -25,7 +24,7 @@ export class LoginComponent {
}
login() {
- this.message = "Trying to log in ...";
+ this.message = 'Trying to log in ...';
this.authService.login().subscribe(() => {
this.setMessage();
diff --git a/public/docs/_examples/router/ts/app/login.routes.ts b/public/docs/_examples/router/ts/app/login.routes.ts
index 71ad3cf9e4..6dabba376f 100644
--- a/public/docs/_examples/router/ts/app/login.routes.ts
+++ b/public/docs/_examples/router/ts/app/login.routes.ts
@@ -4,7 +4,7 @@ import { AuthService } from './auth.service';
import { LoginComponent } from './login.component';
export const LoginRoutes = [
- { path: '/login', component: LoginComponent }
+ { path: 'login', component: LoginComponent }
];
export const AUTH_PROVIDERS = [AuthGuard, AuthService];
diff --git a/public/docs/_examples/security/e2e-spec.ts b/public/docs/_examples/security/e2e-spec.ts
new file mode 100644
index 0000000000..410f1428e1
--- /dev/null
+++ b/public/docs/_examples/security/e2e-spec.ts
@@ -0,0 +1,25 @@
+///
+'use strict';
+describe('Security E2E Tests', () => {
+ beforeAll(function() { browser.get(''); });
+
+ it('sanitizes innerHTML', () => {
+ let interpolated = element(By.className('e2e-inner-html-interpolated'));
+ expect(interpolated.getText())
+ .toContain('Template
Syntax');
+ let bound = element(By.className('e2e-inner-html-bound'));
+ expect(bound.getText()).toContain('Template alert("0wned") Syntax');
+ let bold = element(By.css('.e2e-inner-html-bound b'));
+ expect(bold.getText()).toContain('Syntax');
+ });
+
+ it('binds trusted URLs', () => {
+ let dangerousUrl = element(By.className('e2e-dangerous-url'));
+ expect(dangerousUrl.getAttribute('href')).toMatch(/^javascript:alert/);
+ });
+
+ it('binds trusted resource URLs', () => {
+ let iframe = element(By.className('e2e-iframe'));
+ expect(iframe.getAttribute('src')).toMatch(/^https:\/\/www.youtube.com\//);
+ });
+});
diff --git a/public/docs/_examples/security/ts/app/app.component.ts b/public/docs/_examples/security/ts/app/app.component.ts
new file mode 100644
index 0000000000..153e6b9e49
--- /dev/null
+++ b/public/docs/_examples/security/ts/app/app.component.ts
@@ -0,0 +1,20 @@
+// #docregion
+import { Component } from '@angular/core';
+
+import { BypassSecurityComponent } from './bypass-security.component';
+import { InnerHtmlBindingComponent } from './inner-html-binding.component';
+
+@Component({
+ selector: 'app-root',
+ template: `
+
Security
+
+
+ `,
+ directives: [
+ BypassSecurityComponent,
+ InnerHtmlBindingComponent,
+ ],
+})
+export class AppComponent {
+}
diff --git a/public/docs/_examples/security/ts/app/bypass-security.component.html b/public/docs/_examples/security/ts/app/bypass-security.component.html
new file mode 100644
index 0000000000..75b7734dd4
--- /dev/null
+++ b/public/docs/_examples/security/ts/app/bypass-security.component.html
@@ -0,0 +1,15 @@
+
+
Bypass Security Component
+
+
+
A dangerous URL:
+
Click me.
+
+
+
+
Resource URL:
+
+
+
+
+
diff --git a/public/docs/_examples/security/ts/app/bypass-security.component.ts b/public/docs/_examples/security/ts/app/bypass-security.component.ts
new file mode 100644
index 0000000000..6a5f350d28
--- /dev/null
+++ b/public/docs/_examples/security/ts/app/bypass-security.component.ts
@@ -0,0 +1,33 @@
+// #docplaster
+// #docregion
+import { Component } from '@angular/core';
+import { DomSanitizationService, SafeResourceUrl, SafeUrl } from '@angular/platform-browser';
+
+@Component({
+ selector: 'bypass-security',
+ templateUrl: 'app/bypass-security.component.html',
+})
+export class BypassSecurityComponent {
+ dangerousUrl: SafeUrl;
+ videoUrl: SafeResourceUrl;
+
+ // #docregion trust-url
+ constructor(private sanitizer: DomSanitizationService) {
+ // javascript: URLs are dangerous if attacker controlled. Angular sanitizes them in data
+ // binding, but we can explicitly tell Angular to trust this value:
+ this.dangerousUrl = sanitizer.bypassSecurityTrustUrl('javascript:alert("Hi there")');
+ // #enddocregion trust-url
+ this.updateVideoUrl('PUBnlbjZFAI');
+ }
+
+ // #docregion trust-video-url
+ updateVideoUrl(id: string) {
+ // Appending an ID to a YouTube URL is safe.
+ // Always make sure to construct SafeValue objects as close as possible to the input data, so
+ // that it's easier to check if the value is safe.
+ this.videoUrl =
+ this.sanitizer.bypassSecurityTrustResourceUrl('https://www.youtube.com/embed/' + id);
+ }
+ // #enddocregion trust-video-url
+}
+// #enddocregion
diff --git a/public/docs/_examples/security/ts/app/inner-html-binding.component.html b/public/docs/_examples/security/ts/app/inner-html-binding.component.html
new file mode 100644
index 0000000000..fe540d25fe
--- /dev/null
+++ b/public/docs/_examples/security/ts/app/inner-html-binding.component.html
@@ -0,0 +1,6 @@
+
+
Binding innerHTML
+
Bound value:
+
{{htmlSnippet}}
+
Result of binding to innerHTML:
+
diff --git a/public/docs/_examples/security/ts/app/inner-html-binding.component.ts b/public/docs/_examples/security/ts/app/inner-html-binding.component.ts
new file mode 100644
index 0000000000..95a9f55979
--- /dev/null
+++ b/public/docs/_examples/security/ts/app/inner-html-binding.component.ts
@@ -0,0 +1,14 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ moduleId: module.id,
+ selector: 'inner-html-binding',
+ templateUrl: 'inner-html-binding.component.html',
+})
+// #docregion inner-html-controller
+export class InnerHtmlBindingComponent {
+ // E.g. a user/attacker controlled value from a URL.
+ htmlSnippet = 'Template
Syntax';
+}
+// #enddocregion inner-html-controller
diff --git a/public/docs/_examples/security/ts/app/main.ts b/public/docs/_examples/security/ts/app/main.ts
new file mode 100644
index 0000000000..3e1476beac
--- /dev/null
+++ b/public/docs/_examples/security/ts/app/main.ts
@@ -0,0 +1,8 @@
+// #docregion
+import { bootstrap } from '@angular/platform-browser-dynamic';
+
+// #docregion import
+import { AppComponent } from './app.component';
+// #enddocregion import
+
+bootstrap(AppComponent);
diff --git a/public/docs/_examples/security/ts/example-config.json b/public/docs/_examples/security/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/security/ts/index.html b/public/docs/_examples/security/ts/index.html
new file mode 100644
index 0000000000..b665a8bc6f
--- /dev/null
+++ b/public/docs/_examples/security/ts/index.html
@@ -0,0 +1,26 @@
+
+
+
+
+
Angular Content Security
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Loading...
+
+
diff --git a/public/docs/_examples/security/ts/plnkr.json b/public/docs/_examples/security/ts/plnkr.json
new file mode 100644
index 0000000000..e66527cb6b
--- /dev/null
+++ b/public/docs/_examples/security/ts/plnkr.json
@@ -0,0 +1,8 @@
+{
+ "description": "Content Security",
+ "files": [
+ "!**/*.d.ts",
+ "!**/*.js"
+ ],
+ "tags": ["security"]
+}
diff --git a/public/docs/_examples/style-guide/ts/10-01/app/+dashboard/dashboard.component.ts b/public/docs/_examples/style-guide/ts/10-01/app/+dashboard/dashboard.component.ts
deleted file mode 100644
index 925b3c0365..0000000000
--- a/public/docs/_examples/style-guide/ts/10-01/app/+dashboard/dashboard.component.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-@Component({
- selector: 'toh-dashboard',
- templateUrl: 'app/dashboard/dashboard.component.html'
-})
-export class DashboardComponent implements OnInit {
- constructor() { }
-
- ngOnInit() { }
-}
diff --git a/public/docs/_examples/style-guide/ts/10-01/app/+dashboard/index.ts b/public/docs/_examples/style-guide/ts/10-01/app/+dashboard/index.ts
deleted file mode 100644
index 28b2996161..0000000000
--- a/public/docs/_examples/style-guide/ts/10-01/app/+dashboard/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './dashboard.component';
diff --git a/public/docs/_examples/style-guide/ts/10-01/app/+heroes/heroes.component.ts b/public/docs/_examples/style-guide/ts/10-01/app/+heroes/heroes.component.ts
deleted file mode 100644
index 569a25f688..0000000000
--- a/public/docs/_examples/style-guide/ts/10-01/app/+heroes/heroes.component.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-@Component({
- selector: 'toh-heroes',
- templateUrl: 'app/heroes/heroes.component.html'
-})
-export class HeroesComponent implements OnInit {
- constructor() { }
-
- ngOnInit() { }
-}
diff --git a/public/docs/_examples/style-guide/ts/10-01/app/+heroes/index.ts b/public/docs/_examples/style-guide/ts/10-01/app/+heroes/index.ts
deleted file mode 100644
index d85a0c81c4..0000000000
--- a/public/docs/_examples/style-guide/ts/10-01/app/+heroes/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from './shared';
-export * from './heroes.component.ts';
diff --git a/public/docs/_examples/style-guide/ts/10-01/app/+heroes/shared/hero.service.ts b/public/docs/_examples/style-guide/ts/10-01/app/+heroes/shared/hero.service.ts
deleted file mode 100644
index 6d9ec12949..0000000000
--- a/public/docs/_examples/style-guide/ts/10-01/app/+heroes/shared/hero.service.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { Injectable } from '@angular/core';
-
-@Injectable()
-export class HeroService {
-
- constructor() { }
-
-}
diff --git a/public/docs/_examples/style-guide/ts/10-01/app/+heroes/shared/index.ts b/public/docs/_examples/style-guide/ts/10-01/app/+heroes/shared/index.ts
deleted file mode 100644
index 9edcfff7fb..0000000000
--- a/public/docs/_examples/style-guide/ts/10-01/app/+heroes/shared/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './hero.service';
diff --git a/public/docs/_examples/style-guide/ts/10-01/app/app.component.ts b/public/docs/_examples/style-guide/ts/10-01/app/app.component.ts
deleted file mode 100644
index 38d27afd5d..0000000000
--- a/public/docs/_examples/style-guide/ts/10-01/app/app.component.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-// #docregion
-import { Component } from '@angular/core';
-import { Routes, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from '@angular/router';
-
-import { NavComponent } from './shared';
-import { DashboardComponent } from './+dashboard';
-import { HeroesComponent, HeroService } from './+heroes';
-
-@Component({
- selector: 'toh-app',
- templateUrl: 'app/app.component.html',
- styleUrls: ['app/app.component.css'],
- directives: [ROUTER_DIRECTIVES, NavComponent],
- providers: [
- ROUTER_PROVIDERS,
- HeroService
- ]
-})
-@Routes([
- { path: '/dashboard', component: DashboardComponent }, // , useAsDefault: true}, // coming soon
- { path: '/heroes/...', component: HeroesComponent },
-])
-export class AppComponent {}
diff --git a/public/docs/_examples/style-guide/ts/10-01/app/index.ts b/public/docs/_examples/style-guide/ts/10-01/app/index.ts
deleted file mode 100644
index 033b419bdc..0000000000
--- a/public/docs/_examples/style-guide/ts/10-01/app/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export * from './+dashboard';
-export * from './+heroes';
-export * from './shared';
-export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/10-01/app/shared/index.ts b/public/docs/_examples/style-guide/ts/10-01/app/shared/index.ts
deleted file mode 100644
index 38d6745899..0000000000
--- a/public/docs/_examples/style-guide/ts/10-01/app/shared/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './nav';
diff --git a/public/docs/_examples/style-guide/ts/10-01/app/shared/nav/index.ts b/public/docs/_examples/style-guide/ts/10-01/app/shared/nav/index.ts
deleted file mode 100644
index ca588a3642..0000000000
--- a/public/docs/_examples/style-guide/ts/10-01/app/shared/nav/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './nav.component';
diff --git a/public/docs/_examples/style-guide/ts/10-01/app/shared/nav/nav.component.ts b/public/docs/_examples/style-guide/ts/10-01/app/shared/nav/nav.component.ts
deleted file mode 100644
index 79dc0b0710..0000000000
--- a/public/docs/_examples/style-guide/ts/10-01/app/shared/nav/nav.component.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-@Component({
- selector: 'toh-nav',
- templateUrl: 'app/shared/nav/nav.component.html'
-})
-export class NavComponent implements OnInit {
- constructor() { }
-
- ngOnInit() { }
-
-}
diff --git a/public/docs/_examples/systemjs.config.js b/public/docs/_examples/systemjs.config.js
index ca37f9e8e1..22b0c210cc 100644
--- a/public/docs/_examples/systemjs.config.js
+++ b/public/docs/_examples/systemjs.config.js
@@ -24,6 +24,7 @@
'common',
'compiler',
'core',
+ 'forms',
'http',
'platform-browser',
'platform-browser-dynamic',
diff --git a/public/docs/_examples/systemjs.config.plunker.js b/public/docs/_examples/systemjs.config.plunker.js
index 0479c88703..40758797e6 100644
--- a/public/docs/_examples/systemjs.config.plunker.js
+++ b/public/docs/_examples/systemjs.config.plunker.js
@@ -5,8 +5,9 @@
*/
(function(global) {
- var ngVer = '@2.0.0-rc.2'; // lock in the angular package version; do not let it float to current!
- var routerVer = '@3.0.0-alpha.3'; // lock router version
+ var ngVer = '@2.0.0-rc.3'; // lock in the angular package version; do not let it float to current!
+ var routerVer = '@3.0.0-alpha.7'; // lock router version
+ var formsVer = '@0.1.1'; // lock forms version
//map tells the System loader where to look for things
var map = {
@@ -14,6 +15,7 @@
'@angular': 'https://npmcdn.com/@angular', // sufficient if we didn't pin the version
'@angular/router': 'https://npmcdn.com/@angular/router' + routerVer,
+ '@angular/forms': 'https://npmcdn.com/@angular/forms' + formsVer,
'angular2-in-memory-web-api': 'https://npmcdn.com/angular2-in-memory-web-api', // get latest
'rxjs': 'https://npmcdn.com/rxjs@5.0.0-beta.6',
'ts': 'https://npmcdn.com/plugin-typescript@4.0.10/lib/plugin.js',
@@ -57,6 +59,9 @@
// No umd for router yet
packages['@angular/router'] = { main: 'index.js', defaultExtension: 'js' };
+ // Forms not on rc yet
+ packages['@angular/forms'] = { main: 'index.js', defaultExtension: 'js' };
+
var config = {
// DEMO ONLY! REAL CODE SHOULD NOT TRANSPILE IN THE BROWSER
transpiler: 'ts',
diff --git a/public/docs/_examples/typings.json b/public/docs/_examples/typings.json
index 9125b50685..3385926d1f 100644
--- a/public/docs/_examples/typings.json
+++ b/public/docs/_examples/typings.json
@@ -1,7 +1,7 @@
{
"globalDependencies": {
- "core-js": "registry:dt/core-js#0.0.0+20160317120654",
- "jasmine": "registry:dt/jasmine#2.2.0+20160505161446",
- "node": "registry:dt/node#6.0.0+20160613154055"
+ "core-js": "registry:dt/core-js#0.0.0+20160602141332",
+ "jasmine": "registry:dt/jasmine#2.2.0+20160621224255",
+ "node": "registry:dt/node#6.0.0+20160621231320"
}
}
diff --git a/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/systemjs.config.1.js b/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/systemjs.config.1.js
index a4134235b8..3661eb52a2 100644
--- a/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/systemjs.config.1.js
+++ b/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/systemjs.config.1.js
@@ -25,6 +25,7 @@
'common',
'compiler',
'core',
+ 'forms',
'http',
'platform-browser',
'platform-browser-dynamic',
diff --git a/public/docs/_examples/upgrade-phonecat-3-final/ts/systemjs.config.1.js b/public/docs/_examples/upgrade-phonecat-3-final/ts/systemjs.config.1.js
index a4134235b8..3661eb52a2 100644
--- a/public/docs/_examples/upgrade-phonecat-3-final/ts/systemjs.config.1.js
+++ b/public/docs/_examples/upgrade-phonecat-3-final/ts/systemjs.config.1.js
@@ -25,6 +25,7 @@
'common',
'compiler',
'core',
+ 'forms',
'http',
'platform-browser',
'platform-browser-dynamic',
diff --git a/public/docs/_examples/webpack/ts/package.webpack.json b/public/docs/_examples/webpack/ts/package.webpack.json
index 94e5525bd2..4064a86611 100644
--- a/public/docs/_examples/webpack/ts/package.webpack.json
+++ b/public/docs/_examples/webpack/ts/package.webpack.json
@@ -10,12 +10,13 @@
},
"license": "MIT",
"dependencies": {
- "@angular/common": "2.0.0-rc.2",
- "@angular/compiler": "2.0.0-rc.2",
- "@angular/core": "2.0.0-rc.2",
- "@angular/http": "2.0.0-rc.2",
- "@angular/platform-browser": "2.0.0-rc.2",
- "@angular/platform-browser-dynamic": "2.0.0-rc.2",
+ "@angular/common": "2.0.0-rc.3",
+ "@angular/compiler": "2.0.0-rc.3",
+ "@angular/core": "2.0.0-rc.3",
+ "@angular/forms": "0.1.1",
+ "@angular/http": "2.0.0-rc.3",
+ "@angular/platform-browser": "2.0.0-rc.3",
+ "@angular/platform-browser-dynamic": "2.0.0-rc.3",
"@angular/router-deprecated": "2.0.0-rc.2",
"core-js": "^2.4.0",
"reflect-metadata": "0.1.2",
diff --git a/public/docs/_examples/webpack/ts/typings.1.json b/public/docs/_examples/webpack/ts/typings.1.json
index 9125b50685..3385926d1f 100644
--- a/public/docs/_examples/webpack/ts/typings.1.json
+++ b/public/docs/_examples/webpack/ts/typings.1.json
@@ -1,7 +1,7 @@
{
"globalDependencies": {
- "core-js": "registry:dt/core-js#0.0.0+20160317120654",
- "jasmine": "registry:dt/jasmine#2.2.0+20160505161446",
- "node": "registry:dt/node#6.0.0+20160613154055"
+ "core-js": "registry:dt/core-js#0.0.0+20160602141332",
+ "jasmine": "registry:dt/jasmine#2.2.0+20160621224255",
+ "node": "registry:dt/node#6.0.0+20160621231320"
}
}
diff --git a/public/docs/dart/latest/_data.json b/public/docs/dart/latest/_data.json
index cf4039621f..fe6a0eb5ed 100644
--- a/public/docs/dart/latest/_data.json
+++ b/public/docs/dart/latest/_data.json
@@ -3,7 +3,7 @@
"icon": "home",
"title": "Angular Docs",
"menuTitle": "Docs Home",
- "banner": "Welcome to
angular.io/dart! The current Angular 2 release is
beta.17. Consult the
Change Log about recent enhancements, fixes, and breaking changes."
+ "banner": "Welcome to
angular.io/dart! The current Angular 2 Dart release is
beta.17. Consult the
Change Log about recent enhancements, fixes, and breaking changes."
},
"quickstart": {
diff --git a/public/docs/dart/latest/guide/_data.json b/public/docs/dart/latest/guide/_data.json
index eed8cf36a4..7d25438019 100644
--- a/public/docs/dart/latest/guide/_data.json
+++ b/public/docs/dart/latest/guide/_data.json
@@ -73,6 +73,11 @@
"intro": "Learn how to apply CSS styles to components."
},
+ "security": {
+ "title": "Security",
+ "intro": "Prevent security vulnerabilities"
+ },
+
"hierarchical-dependency-injection": {
"title": "Hierarchical Dependency Injectors",
"navTitle": "Hierarchical Injectors",
diff --git a/public/docs/dart/latest/guide/security.jade b/public/docs/dart/latest/guide/security.jade
new file mode 100644
index 0000000000..f8df2a84a6
--- /dev/null
+++ b/public/docs/dart/latest/guide/security.jade
@@ -0,0 +1 @@
+!= partial("../../../_includes/_ts-temp")
\ No newline at end of file
diff --git a/public/docs/js/latest/_data.json b/public/docs/js/latest/_data.json
index 0eb181d060..5227a9dd16 100644
--- a/public/docs/js/latest/_data.json
+++ b/public/docs/js/latest/_data.json
@@ -3,7 +3,7 @@
"icon": "home",
"title": "Angular Docs",
"menuTitle": "Docs Home",
- "banner": "Welcome to
Angular in JavaScript! The current Angular 2 release is
rc.2. Please consult the
Change Log about recent enhancements, fixes, and breaking changes."
+ "banner": "Welcome to
Angular in JavaScript! The current Angular 2 release is
rc.3. Please consult the
Change Log about recent enhancements, fixes, and breaking changes."
},
"quickstart": {
diff --git a/public/docs/js/latest/guide/_data.json b/public/docs/js/latest/guide/_data.json
index 939d7c5c4a..a0f8270eec 100644
--- a/public/docs/js/latest/guide/_data.json
+++ b/public/docs/js/latest/guide/_data.json
@@ -29,7 +29,7 @@
"basics": true
},
- "forms": {
+ "forms-deprecated": {
"title": "Forms",
"intro": "A form creates a cohesive, effective, and compelling data entry experience. An Angular form coordinates a set of data-bound user controls, tracks changes, validates input, and presents errors.",
"nextable": true,
@@ -73,6 +73,11 @@
"intro": "Learn how to apply CSS styles to components."
},
+ "security": {
+ "title": "Security",
+ "intro": "Prevent security vulnerabilities"
+ },
+
"hierarchical-dependency-injection": {
"title": "Hierarchical Dependency Injectors",
"navTitle": "Hierarchical Injectors",
diff --git a/public/docs/js/latest/guide/forms-deprecated.jade b/public/docs/js/latest/guide/forms-deprecated.jade
new file mode 100644
index 0000000000..185b25dee5
--- /dev/null
+++ b/public/docs/js/latest/guide/forms-deprecated.jade
@@ -0,0 +1,648 @@
+include ../_util-fns
+
+.alert.is-important
+ :marked
+ This guide is using the deprecated forms API.
+
+ We have created a new version using the new API
here.
+
+:marked
+ We’ve all used a form to login, submit a help request, place an order, book a flight,
+ schedule a meeting and perform countless other data entry tasks.
+ Forms are the mainstay of business applications.
+
+ Any seasoned web developer can slap together an HTML form with all the right tags.
+ It's more challenging to create a cohesive data entry experience that guides the
+ user efficiently and effectively through the workflow behind the form.
+
+ *That* takes design skills that are, to be frank, well out of scope for this chapter.
+
+ It also takes framework support for
+ **two-way data binding, change tracking, validation, and error handling**
+ ... which we shall cover in this chapter on Angular forms.
+
+ We will build a simple form from scratch, one step at a time. Along the way we'll learn
+
+ - How to build an Angular form with a component and template
+
+ - The `ngModel` two-way data binding syntax for reading and writing values to input controls
+
+ - The `ngControl` directive to track the change state and validity of form controls
+
+ - The special CSS classes that `ngControl` adds to form controls and how we can use them to provide strong visual feedback
+
+ - How to display validation errors to users and enable/disable form controls
+
+ - How to share information across controls with template local variables
+
+ [Live Example](/resources/live-examples/forms-deprecated/js/plnkr.html)
+.l-main-section
+:marked
+ ## Template-Driven Forms
+
+ Many of us will build forms by writing templates in the Angular [template syntax](./template-syntax.html) with
+ the form-specific directives and techniques described in this chapter.
+.l-sub-section
+ :marked
+ That's not the only way to create a form but it's the way we'll cover in this chapter.
+:marked
+ We can build almost any form we need with an Angular template — login forms, contact forms ... pretty much any business forms.
+ We can lay out the controls creatively, bind them to data, specify validation rules and display validation errors,
+ conditionally enable or disable specific controls, trigger built-in visual feedback, and much more.
+
+ It will be pretty easy because Angular handles many of the repetitive, boiler plate tasks we'd
+ otherwise wrestle with ourselves.
+
+ We'll discuss and learn to build the following template-driven form:
+
+figure.image-display
+ img(src="/resources/images/devguide/forms/hero-form-1.png" width="400px" alt="Clean Form")
+
+:marked
+ Here at the *Hero Employment Agency* we use this form to maintain personal information about the
+ heroes in our stable. Every hero needs a job. It's our company mission to match the right hero with the right crisis!
+
+ Two of the three fields on this form are required. Required fields have a green bar on the left to make them easy to spot.
+
+ If we delete the hero name, the form displays a validation error in an attention grabbing style:
+
+figure.image-display
+ img(src="/resources/images/devguide/forms/hero-form-2.png" width="400px" alt="Invalid, Name Required")
+
+:marked
+ Note that the submit button is disabled and the "required" bar to the left of the input control changed from green to red.
+
+.l-sub-section
+ p We'll' customize the colors and location of the "required" bar with standard CSS.
+
+:marked
+ We will build this form in the following sequence of small steps
+
+ 1. Create the `Hero` model class
+ 1. Create the component that controls the form
+ 1. Create a template with the initial form layout
+ 1. Add the **ngModel** directive to each form input control
+ 1. Add the **ngControl** directive to each form input control
+ 1. Add custom CSS to provide visual feedback
+ 1. Show and hide validation error messages
+ 1. Handle form submission with **ngSubmit**
+ 1. Disable the form’s submit button until the form is valid
+
+:marked
+ ## Setup
+ Create a new project folder (`angular2-forms`) and follow the steps in the [QuickStart](../quickstart.html).
+
+ ## Create the Hero Model Class
+
+ As users enter form data, we capture their changes and update an instance of a model.
+ We can't layout the form until we know what the model looks like.
+
+ A model can be as simple as a "property bag" that holds facts about a thing of application importance.
+ That describes well our `Hero` class with its three required fields (`id`, `name`, `power`)
+ and one optional field (`alterEgo`).
+
+ Create a new file in the app folder called `hero.js` and give it the following constructor:
+
++makeExample('forms-deprecated/js/app/hero.js', null, 'app/hero.js')
+
+:marked
+ It's an anemic model with few requirements and no behavior. Perfect for our demo.
+
+ The `alterEgo` is optional and the constructor lets us omit it by being the last argument.
+
+ We can create a new hero like this:
+code-example(format="").
+ var myHero = new Hero(42, 'SkyDog',
+ 'Fetch any object at any distance', 'Leslie Rollover');
+ console.log('My hero is called ' + myHero.name); // "My hero is called SkyDog"
+:marked
+ We update the `` of the `index.html` to include this javascript file.
+
++makeExample('forms-deprecated/js/index.html', 'scripts-hero', 'index.html (excerpt)')(format=".")
+
+.l-main-section
+:marked
+ ## Create a Form component
+
+ An Angular form has two parts: an HTML-based template and a code-based Component to handle data and user interactions.
+
+ We begin with the Component because it states, in brief, what the Hero editor can do.
+
+ Create a new file called `hero-form.component.js` and give it the following definition:
+
++makeExample('forms-deprecated/js/app/hero-form.component.js', 'first', 'app/hero-form.component.js')
+
+:marked
+ There’s nothing special about this component, nothing form-specific, nothing to distinguish it from any component we've written before.
+
+ Understanding this component requires only the Angular 2 concepts we’ve learned in previous chapters
+
+ 1. We use the `ng.core` object from the Angular library as we usually do.
+
+ 1. The `Component()` selector value of "hero-form" means we can drop this form in a parent template with a `
` tag.
+
+ 1. The `templateUrl` property points to a separate file for template HTML called `hero-form.component.html`.
+
+ 1. We defined dummy data for `model` and `powers` as befits a demo.
+ Down the road, we can inject a data service to get and save real data
+ or perhaps expose these properties as [inputs and outputs](./template-syntax.html#inputs-outputs) for binding to a
+ parent component. None of this concerns us now and these future changes won't affect our form.
+
+ 1. We threw in a `diagnostic` method at the end to return a JSON representation of our model.
+ It'll help us see what we're doing during our development; we've left ourselves a cleanup note to discard it later.
+
+ Why don't we write the template inline in the component file as we often do
+ elsewhere in the Developer Guide?
+
+ There is no “right” answer for all occasions. We like inline templates when they are short.
+ Most form templates won't be short. TypeScript and JavaScript files generally aren't the best place to
+ write (or read) large stretches of HTML and few editors are much help with files that have a mix of HTML and code.
+ We also like short files with a clear and obvious purpose like this one.
+
+ We made a good choice to put the HTML template elsewhere.
+ We'll write that template in a moment. Before we do, we'll take a step back
+ and revise the `app.component.js` to make use of our new `HeroFormComponent`.
+
+:marked
+ Again we update the `` of the `index.html` to include the new javascript file.
+
++makeExample('forms-deprecated/js/index.html', 'scripts-hero-form', 'index.html (excerpt)')(format=".")
+
+.l-main-section
+:marked
+ ## Revise the *app.component.js*
+
+ `app.component.js` is the application's root component. It will host our new `HeroFormComponent`.
+
+ Replace the contents of the "QuickStart" version with the following:
++makeExample('forms-deprecated/js/app/app.component.js', null, 'app/app.component.js')
+
+:marked
+.l-sub-section
+ :marked
+ There are only two changes:
+
+ 1. The `template` is simply the new element tag identified by the component's `select` property.
+
+ 1. The `directives` array tells Angular that our template depends upon the `HeroFormComponent`
+ which is itself a Directive (as are all Components).
+
+.l-main-section
+:marked
+ ## Create an initial HTML Form Template
+
+ Create a new template file called `hero-form.component.html` and give it the following definition:
+
++makeExample('forms-deprecated/js/app/hero-form.component.html', 'start', 'app/hero-form.component.html')
+
+:marked
+ That is plain old HTML 5. We're presenting two of the `Hero` fields, `name` and `alterEgo`, and
+ opening them up for user input in input boxes.
+
+ The *Name* `` control has the HTML5 `required` attribute;
+ the *Alter Ego* `` control does not because `alterEgo` is optional.
+
+ We've got a *Submit* button at the bottom with some classes on it.
+
+ **We are not using Angular yet**. There are no bindings. No extra directives. Just layout.
+
+ The `container`,`form-group`, `form-control`, and `btn` classes
+ come from [Twitter Boostrap](http://getbootstrap.com/css/). Purely cosmetic.
+ We're using Bootstrap to gussy up our form.
+ Hey, what's a form without a little style!
+
+.callout.is-important
+ header Angular Forms Do Not Require A Style Library
+ :marked
+ Angular makes no use of the `container`, `form-group`, `form-control`, and `btn` classes or
+ the styles of any external library. Angular apps can use any CSS library
+ ... or none at all.
+
+:marked
+ Let's add the stylesheet.
+
+ol
+ li Open a terminal window in the application root folder and enter the command:
+ code-example(language="html" escape="html").
+ npm install bootstrap --save
+ li Open index.html and add the following link to the <head>.
+ +makeExample('forms-deprecated/js/index.html', 'bootstrap')(format=".")
+:marked
+.l-main-section
+:marked
+ ## Add Powers with ***ngFor**
+ Our hero may choose one super power from a fixed list of Agency-approved powers.
+ We maintain that list internally (in `HeroFormComponent`).
+
+ We'll add a `select` to our
+ form and bind the options to the `powers` list using `NgFor`,
+ a technique we might have seen before in the [Displaying Data](./displaying-data.html) chapter.
+
+ Add the following HTML *immediately below* the *Alter Ego* group.
++makeExample('forms-deprecated/js/app/hero-form.component.html', 'powers', 'app/hero-form.component.html (excerpt)')(format=".")
+
+:marked
+ We are repeating the `` tag for each power in the list of Powers.
+ The `#p` local template variable is a different power in each iteration;
+ we display its name using the interpolation syntax with the double-curly-braces.
+
+.l-main-section
+:marked
+ ## Two-way data binding with ***ngModel**
+ Running the app right now would be disappointing.
+
+figure.image-display
+ img(src="/resources/images/devguide/forms/hero-form-3.png" width="400px" alt="Early form with no binding")
+:marked
+ We don't see hero data because we are not binding to the `Hero` yet.
+ We know how to do that from earlier chapters.
+ [Displaying Data](./displaying-data.html) taught us Property Binding.
+ [User Input](./user-input.html) showed us how to listen for DOM events with an
+ Event Binding and how to update a component property with the displayed value.
+
+ Now we need to display, listen, and extract at the same time.
+
+ We could use those techniques again in our form.
+ Instead we'll introduce something new, the `NgModel` directive, that
+ makes binding our form to the model super-easy.
+
+ Find the `` tag for the "Name" and update it like this
+
++makeExample('forms-deprecated/js/app/hero-form.component.html', 'ngModel-1','app/hero-form.component.html (excerpt)')(format=".")
+
+.l-sub-section
+ :marked
+ We appended a diagnostic interpolation after the input tag
+ so we can see what we're doing.
+ We left ourselves a note to throw it way when we're done.
+
+:marked
+ Focus on the binding syntax: `[(ngModel)]="..."`.
+
+ If we ran the app right now and started typing in the *Name* input box,
+ adding and deleting characters, we'd see them appearing and disappearing
+ from the interpolated text.
+ At some point it might look like this.
+figure.image-display
+ img(src="/resources/images/devguide/forms/ng-model-in-action.png" width="400px" alt="ngModel in action")
+:marked
+ The diagnostic is evidence that we really are flowing values from the input box to the model and
+ back again. **That's two-way data binding!**
+
+ Let's add similar `[(ngModel)]` bindings to *Alter Ego* and *Hero Power*.
+ We'll ditch the input box binding message
+ and add a new binding at the top to the component's `diagnostic` method.
+ Then we can confirm that two-way data binding works *for the entire Hero model*.
+
+ After revision the core of our form should have three `[(ngModel)]` bindings that
+ look much like this:
+
++makeExample('forms-deprecated/js/app/hero-form.component.html', 'ngModel-2', 'app/hero-form.component.html (excerpt)')
+
+:marked
+ If we ran the app right now and changed every Hero model property, the form might display like this:
+figure.image-display
+ img(src="/resources/images/devguide/forms/ng-model-in-action-2.png" width="400px" alt="ngModel in super action")
+:marked
+ The diagnostic near the top of the form
+ confirms that all of our changes are reflected in the model.
+
+ **Delete** the `{{diagnostic()}}` binding at the top as it has served its purpose.
+
+.l-sub-section
+ :marked
+ ### Inside [(ngModel)]
+ *This section is an optional deep dive into [(ngModel)]. Not interested? Skip ahead!*
+
+ The punctuation in the binding syntax, [()], is a good clue to what's going on.
+
+ In a Property Binding, a value flows from the model to a target property on screen.
+ We identify that target property by surrounding its name in brackets, [].
+ This is a one-way data binding **from the model to the view**.
+
+ In an Event Binding, we flow the value from the target property on screen to the model.
+ We identify that target property by surrounding its name in parentheses, ().
+ This is a one-way data binding in the opposite direction **from the view to the model**.
+
+ No wonder Angular chose to combine the punctuation as [()]
+ to signify a two-way data binding and a **flow of data in both directions**.
+
+ In fact, we can break the `NgModel` binding into its two separate modes
+ as we do in this re-write of the "Name" `` binding:
+ +makeExample('forms-deprecated/js/app/hero-form.component.html', 'ngModel-3','app/hero-form.component.html (excerpt)')(format=".")
+
+ :marked
+
The Property Binding should feel familiar. The Event Binding might seem strange.
+
+ The `ngModelChange` is not an `` element event.
+ It is actually an event property of the `NgModel` directive.
+ When Angular sees a binding target in the form [(x)],
+ it expects the `x` directive to have an `x` input property and an `xChange` output property.
+
+ The other oddity is the template expression, `model.name = $event`.
+ We're used to seeing an `$event` object coming from a DOM event.
+ The `ngModelChange` property doesn't produce a DOM event; it's an Angular `EventEmitter`
+ property that returns the input box value when it fires — which is precisely what
+ we should assign to the model's `name' property.
+
+ Nice to know but is it practical? We almost always prefer `[(ngModel)]`.
+ We might split the binding if we had to do something special in
+ the event handling such as debounce or throttle the key strokes.
+
+ Learn more about `NgModel` and other template syntax in the
+ [Template Syntax](./template-syntax.html) chapter.
+
+.l-main-section
+:marked
+ ## Track change-state and validity with **ngControl**
+
+ A form isn't just about data binding. We'd also like to know the state of the controls on our form.
+ The `NgControl` directive keeps track of control state for us.
+
+.callout.is-helpful
+ header NgControl requires Form
+ :marked
+ The `NgControl` is one of a family of `NgForm` directives that can only be applied to
+ a control within a `