docs(forms): code shuffle, text, & alpha52 updates

closes #478
This commit is contained in:
Ward Bell 2015-12-11 11:50:57 -08:00
parent d33a381a6c
commit 4084a725fd
15 changed files with 195 additions and 197 deletions

View File

@ -1,5 +1,5 @@
// #docregion // #docregion
import {bootstrap, Component} from 'angular2/angular2' import {Component} from 'angular2/core';
import {HeroFormComponent} from './hero-form.component' import {HeroFormComponent} from './hero-form.component'
@Component({ @Component({
@ -7,6 +7,4 @@ import {HeroFormComponent} from './hero-form.component'
template: '<hero-form></hero-form>', template: '<hero-form></hero-form>',
directives: [HeroFormComponent] directives: [HeroFormComponent]
}) })
class AppComponent { } export class AppComponent { }
bootstrap(AppComponent);

View File

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

View File

@ -4,16 +4,16 @@
<!-- #docregion edit-div --> <!-- #docregion edit-div -->
<div [hidden]="submitted"> <div [hidden]="submitted">
<h1>Hero Form</h1> <h1>Hero Form</h1>
<!-- #docregion ng-submit --> <!-- #docregion ngSubmit -->
<form (ng-submit)="onSubmit()" #hf="form"> <form (ngSubmit)="onSubmit()" #heroForm="ngForm">
<!-- #enddocregion ngSubmit -->
<!-- #enddocregion edit-div --> <!-- #enddocregion edit-div -->
<!-- #enddocregion ng-submit -->
<div class="form-group"> <div class="form-group">
<label for="name">Name</label> <label for="name">Name</label>
<!-- #docregion name-with-error-msg --> <!-- #docregion name-with-error-msg -->
<input type="text" class="form-control" required <input type="text" class="form-control" required
[(ng-model)]="model.name" [(ngModel)]="model.name"
ng-control="name" #name="form" > ngControl="name" #name="ngForm" >
<div [hidden]="name.valid" class="alert alert-danger"> <div [hidden]="name.valid" class="alert alert-danger">
Name is required Name is required
</div> </div>
@ -23,16 +23,16 @@
<div class="form-group"> <div class="form-group">
<label for="alterEgo">Alter Ego</label> <label for="alterEgo">Alter Ego</label>
<input type="text" class="form-control" <input type="text" class="form-control"
[(ng-model)]="model.alterEgo" [(ngModel)]="model.alterEgo"
ng-control="alterEgo" > ngControl="alterEgo" >
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="power">Hero Power</label> <label for="power">Hero Power</label>
<select class="form-control" required <select class="form-control" required
[(ng-model)]="model.power" [(ngModel)]="model.power"
ng-control="power" #power="form" > ngControl="power" #power="ngForm" >
<option *ng-for="#p of powers" [value]="p">{{p}}</option> <option *ngFor="#p of powers" [value]="p">{{p}}</option>
</select> </select>
<div [hidden]="power.valid" class="alert alert-danger"> <div [hidden]="power.valid" class="alert alert-danger">
Power is required Power is required
@ -41,7 +41,7 @@
<!-- #docregion submit-button --> <!-- #docregion submit-button -->
<button type="submit" class="btn btn-default" <button type="submit" class="btn btn-default"
[disabled]="!hf.form.valid">Submit</button> [disabled]="!heroForm.form.valid">Submit</button>
<!-- #enddocregion submit-button --> <!-- #enddocregion submit-button -->
</form> </form>
</div> </div>
@ -110,7 +110,7 @@
<div class="form-group"> <div class="form-group">
<label for="power">Hero Power</label> <label for="power">Hero Power</label>
<select class="form-control" required> <select class="form-control" required>
<option *ng-for="#p of powers" [value]="p">{{p}}</option> <option *ngFor="#p of powers" [value]="p">{{p}}</option>
</select> </select>
</div> </div>
@ -128,29 +128,29 @@
<div class="container"> <div class="container">
<h1>Hero Form</h1> <h1>Hero Form</h1>
<form> <form>
<!-- #docregion ng-model-2--> <!-- #docregion ngModel-2-->
{{diagnostic}} {{diagnostic}}
<div class="form-group"> <div class="form-group">
<label for="name">Name</label> <label for="name">Name</label>
<input type="text" class="form-control" required <input type="text" class="form-control" required
[(ng-model)]="model.name" > [(ngModel)]="model.name" >
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="alterEgo">Alter Ego</label> <label for="alterEgo">Alter Ego</label>
<input type="text" class="form-control" <input type="text" class="form-control"
[(ng-model)]="model.alterEgo"> [(ngModel)]="model.alterEgo">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="power">Hero Power</label> <label for="power">Hero Power</label>
<select class="form-control" required <select class="form-control" required
[(ng-model)]="model.power" > [(ngModel)]="model.power" >
<option *ng-for="#p of powers" [value]="p">{{p}}</option> <option *ngFor="#p of powers" [value]="p">{{p}}</option>
</select> </select>
</div> </div>
<!-- #enddocregion ng-model-2--> <!-- #enddocregion ngModel-2-->
<button type="submit" class="btn btn-default">Submit</button> <button type="submit" class="btn btn-default">Submit</button>
</form> </form>
@ -159,37 +159,37 @@
<!-- EXTRA MATERIAL FOR DOCUMENTATION --> <!-- EXTRA MATERIAL FOR DOCUMENTATION -->
<hr> <hr>
<!-- #docregion ng-model-1--> <!-- #docregion ngModel-1-->
<input type="text" class="form-control" required <input type="text" class="form-control" required
[(ng-model)]="model.name" > [(ngModel)]="model.name" >
TODO: remove this: {{model.name}} TODO: remove this: {{model.name}}
<!-- #enddocregion ng-model-1--> <!-- #enddocregion ngModel-1-->
<hr> <hr>
<!-- #docregion ng-model-3--> <!-- #docregion ngModel-3-->
<input type="text" class="form-control" required <input type="text" class="form-control" required
[ng-model]="model.name" [ngModel]="model.name"
(ng-model-change)="model.name = $event" > (ngModelChange)="model.name = $event" >
TODO: remove this: {{model.name}} TODO: remove this: {{model.name}}
<!-- #enddocregion ng-model-3--> <!-- #enddocregion ngModel-3-->
<hr> <hr>
<form> <form>
<!-- #docregion ng-control-1 --> <!-- #docregion ngControl-1 -->
<input type="text" class="form-control" required <input type="text" class="form-control" required
[(ng-model)]="model.name" [(ngModel)]="model.name"
ng-control="name" > ngControl="name" >
<!-- #enddocregion ng-control-1 --> <!-- #enddocregion ngControl-1 -->
<hr> <hr>
<!-- #docregion ng-control-2 --> <!-- #docregion ngControl-2 -->
<input type="text" class="form-control" required <input type="text" class="form-control" required
[(ng-model)]="model.name" [(ngModel)]="model.name"
ng-control="name" #spy > ngControl="name" #spy >
<br>TODO: remove this: {{spy.className}} <br>TODO: remove this: {{spy.className}}
<!-- #enddocregion ng-control-2 --> <!-- #enddocregion ngControl-2 -->
</form> </form>
<div> <div>
<hr> <hr>
Name via form.controls = {{showFormControls(hf)}} Name via form.controls = {{showFormControls(heroForm)}}
</div> </div>
</div> </div>

View File

@ -1,8 +1,8 @@
// #docplaster // #docplaster
// #docregion // #docregion
// #docregion first, final // #docregion first, final
import {Component} from 'angular2/angular2'; import {Component} from 'angular2/core';
import {NgForm} from 'angular2/common';
import { Hero } from './hero'; import { Hero } from './hero';
@Component({ @Component({
@ -32,10 +32,10 @@ export class HeroFormComponent {
// Reveal in html: // Reveal in html:
// AlterEgo via form.controls = {{showFormControls(hf)}} // AlterEgo via form.controls = {{showFormControls(hf)}}
showFormControls(form){ showFormControls(form:NgForm){
return form.controls.alterEgo && return form.controls['alterEgo'] &&
// #docregion form-controls // #docregion form-controls
form.controls.name.value; // Dr. IQ form.controls['name'].value; // Dr. IQ
// #enddocregion form-controls // #enddocregion form-controls
} }
///////////////////////////// /////////////////////////////

View File

@ -5,19 +5,19 @@
<head> <head>
<title>Hero Form</title> <title>Hero Form</title>
<!-- #docregion bootstrap --> <!-- #docregion bootstrap -->
<link rel="stylesheet" href="../node_modules/bootstrap/dist/css/bootstrap.min.css"> <link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">
<!-- #enddocregion bootstrap --> <!-- #enddocregion bootstrap -->
<!-- #docregion styles --> <!-- #docregion styles -->
<link rel="stylesheet" href="styles.css"> <link rel="stylesheet" href="styles.css">
<!-- #enddocregion styles --> <!-- #enddocregion styles -->
<script src="../node_modules/systemjs/dist/system.src.js"></script> <script src="/node_modules/systemjs/dist/system.src.js"></script>
<script src="../node_modules/angular2/bundles/angular2.dev.js"></script> <script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<script> <script>
System.config({ System.config({
packages: {'app': {defaultExtension: 'js'}} packages: {'app': {defaultExtension: 'js'}}
}); });
System.import('app/app'); System.import('app/boot');
</script> </script>
</head> </head>

View File

@ -4,19 +4,12 @@
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"tsc": "tsc -p src -w", "tsc": "tsc",
"start": "live-server --open=src" "tsc:w": "tsc -w",
"lite": "lite-server",
"both": "concurrent \"npm run tsc:w\" \"npm run lite\" "
}, },
"keywords": [], "keywords": [],
"author": "", "author": "",
"license": "ISC", "license": "ISC"
"dependencies": {
"angular2": "2.0.0-alpha.44",
"bootstrap": "^3.3.5",
"systemjs": "0.19.2"
},
"devDependencies": {
"live-server": "^0.8.1",
"typescript": "^1.6.2"
}
} }

View File

@ -1,11 +0,0 @@
{
"compilerOptions": {
"target": "ES5",
"module": "commonjs",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": false
}
}

View File

@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "ES5",
"module": "system",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true
},
"exclude": [
"node_modules"
]
}

View File

@ -15,13 +15,14 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"angular2": "^2.0.0-alpha.51", "angular2": "2.0.0-alpha.52",
"systemjs": "0.19.6", "systemjs": "0.19.6",
"es6-promise": "^3.0.2", "es6-promise": "^3.0.2",
"es6-shim": "^0.33.3", "es6-shim": "^0.33.3",
"reflect-metadata": "0.1.2", "reflect-metadata": "0.1.2",
"rxjs": "5.0.0-alpha.14", "rxjs": "5.0.0-alpha.14",
"zone.js": "0.5.8" "zone.js": "0.5.8",
"bootstrap": "^3.3.6"
}, },
"devDependencies": { "devDependencies": {
"concurrently": "^1.0.0", "concurrently": "^1.0.0",

View File

@ -1,6 +1,5 @@
include ../../../../_includes/_util-fns include ../../../../_includes/_util-fns
<!-- http://plnkr.co/edit/wg154K -->
:marked :marked
Weve all used a form to login, submit a help request, place an order, book a flight, Weve 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. schedule a meeting and perform countless other data entry tasks.
@ -20,17 +19,17 @@ include ../../../../_includes/_util-fns
- How to build an Angular form with a component and template - How to build an Angular form with a component and template
- The `ng-model` two-way data binding syntax for reading and writing values to input controls - The `ngModel` two-way data binding syntax for reading and writing values to input controls
- The `ng-control` directive to track the change state and validity of form controls - The `ngControl` directive to track the change state and validity of form controls
- The special CSS classes that `ng-control` adds to form controls and how we can use them to provide strong visual feedback - 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 display validation errors to users and enable/disable form controls
- How to share information across controls with template local variables - How to share information across controls with template local variables
[Live Example](/resources/live-examples/forms/ts/src/plnkr.html) [Live Example](/resources/live-examples/forms/ts/plnkr.html)
.l-main-section .l-main-section
:marked :marked
## Template-Driven Forms ## Template-Driven Forms
@ -51,7 +50,7 @@ include ../../../../_includes/_util-fns
We'll discuss and learn to build the following template-driven form: We'll discuss and learn to build the following template-driven form:
figure.image-display figure.image-display
img(src="/resources/images/devguide/forms/hf-1.png" width="400px" alt="Clean Form") img(src="/resources/images/devguide/forms/heroForm-1.png" width="400px" alt="Clean Form")
:marked :marked
Here at the *Hero Employment Agency* we use this form to maintain personal information about the Here at the *Hero Employment Agency* we use this form to maintain personal information about the
@ -62,7 +61,7 @@ figure.image-display
If we delete the hero name, the form displays a validation error in an attention grabbing style: If we delete the hero name, the form displays a validation error in an attention grabbing style:
figure.image-display figure.image-display
img(src="/resources/images/devguide/forms/hf-2.png" width="400px" alt="Invalid, Name Required") img(src="/resources/images/devguide/forms/heroForm-2.png" width="400px" alt="Invalid, Name Required")
:marked :marked
Note that the submit button is disabled and the "required" bar to the left of the input control changed from green to red. Note that the submit button is disabled and the "required" bar to the left of the input control changed from green to red.
@ -76,11 +75,11 @@ figure.image-display
1. Create the `Hero` model class 1. Create the `Hero` model class
1. Create the component that controls the form 1. Create the component that controls the form
1. Create a template with the initial form layout 1. Create a template with the initial form layout
1. Add the **ng-model** directive to each form input control 1. Add the **ngModel** directive to each form input control
1. Add the **ng-control** 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. Add custom CSS to provide visual feedback
1. Show and hide validation error messages 1. Show and hide validation error messages
1. Handle form submission with **ng-submit** 1. Handle form submission with **ngSubmit**
1. Disable the forms submit button until the form is valid 1. Disable the forms submit button until the form is valid
:marked :marked
@ -98,7 +97,7 @@ figure.image-display
Create a new file in the app folder called `hero.ts` and give it the following class definition: Create a new file in the app folder called `hero.ts` and give it the following class definition:
+makeExample('forms/ts/src/app/hero.ts', null, 'app/hero.ts') +makeExample('forms/ts/app/hero.ts', null, 'app/hero.ts')
:marked :marked
It's an anemic model with few requirements and no behavior. Perfect for our demo. It's an anemic model with few requirements and no behavior. Perfect for our demo.
@ -109,10 +108,11 @@ figure.image-display
The `alterEgo` is optional and the constructor lets us omit it; note the (?) in `alterEgo?`. The `alterEgo` is optional and the constructor lets us omit it; note the (?) in `alterEgo?`.
We can create a new hero like this: We can create a new hero like this:
``` code-example(format="").
let myHero = new Hero(42, 'SkyDog', 'Fetch any object at any distance', 'Leslie Rollover'); let 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" console.log('My hero is called ' + myHero.name); // "My hero is called SkyDog"
``` :marked
.l-main-section .l-main-section
:marked :marked
@ -124,7 +124,7 @@ figure.image-display
Create a new file called `hero-form.component.ts` and give it the following definition: Create a new file called `hero-form.component.ts` and give it the following definition:
+makeExample('forms/ts/src/app/hero-form.component.ts', 'first', 'app/hero-form.component.ts') +makeExample('forms/ts/app/hero-form.component.ts', 'first', 'app/hero-form.component.ts')
:marked :marked
Theres nothing special about this component, nothing form-specific, nothing to distinguish it from any component we've written before. Theres nothing special about this component, nothing form-specific, nothing to distinguish it from any component we've written before.
@ -155,16 +155,16 @@ figure.image-display
We made a good choice to put the HTML template elsewhere. 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 We'll write that template in a moment. Before we do, we'll take a step back
and revise the `app.ts` to make use of our new `HeroFormComponent`. and revise the `app.component.ts` to make use of our new `HeroFormComponent`.
.l-main-section .l-main-section
:marked :marked
## Revise the *app.ts* ## Revise the *app.component.ts*
`app.ts` is the application's root component. It will host our new `HeroFormComponent`. `app.component.ts` is the application's root component. It will host our new `HeroFormComponent`.
Replace the contents of the "QuickStart" version with the following: Replace the contents of the "QuickStart" version with the following:
+makeExample('forms/ts/src/app/app.ts', null, 'app/app.ts') +makeExample('forms/ts/app/app.component.ts', null, 'app/app.component.ts')
:marked :marked
.l-sub-section .l-sub-section
@ -184,7 +184,7 @@ figure.image-display
Create a new template file called `hero-form.component.html` and give it the following definition: Create a new template file called `hero-form.component.html` and give it the following definition:
+makeExample('forms/ts/src/app/hero-form.component.html', 'start', 'app/hero-form.component.html') +makeExample('forms/ts/app/hero-form.component.html', 'start', 'app/hero-form.component.html')
:marked :marked
That is plain old HTML 5. We're presenting two of the `Hero` fields, `name` and `alterEgo`, and That is plain old HTML 5. We're presenting two of the `Hero` fields, `name` and `alterEgo`, and
@ -213,15 +213,15 @@ figure.image-display
Let's add the stylesheet. Let's add the stylesheet.
ol ol
li Open a terminal window and enter the command: li Open a terminal window in the application root folder and enter the command:
code-example(language="html" escape="html"). code-example(language="html" escape="html").
npm install bootstrap --save npm install bootstrap --save
li Open <code>index.html</code> and add the following link to the <code>&lt;head></code>. li Open <code>index.html</code> and add the following link to the <code>&lt;head></code>.
+makeExample('forms/ts/src/index.html', 'bootstrap')(format=".") +makeExample('forms/ts/index.html', 'bootstrap')(format=".")
:marked :marked
.l-main-section .l-main-section
:marked :marked
## Add Powers with ***ng-for** ## Add Powers with ***ngFor**
Our hero may choose one super power from a fixed list of Agency-approved powers. Our hero may choose one super power from a fixed list of Agency-approved powers.
We maintain that list internally (in `HeroFormComponent`). We maintain that list internally (in `HeroFormComponent`).
@ -230,7 +230,7 @@ ol
a technique we might have seen before in the [Displaying Data](./displaying-data.html) chapter. 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. Add the following HTML *immediately below* the *Alter Ego* group.
+makeExample('forms/ts/src/app/hero-form.component.html', 'powers', 'app/hero-form.component.html (excerpt)') +makeExample('forms/ts/app/hero-form.component.html', 'powers', 'app/hero-form.component.html (excerpt)')(format=".")
:marked :marked
We are repeating the `<options>` tag for each power in the list of Powers. We are repeating the `<options>` tag for each power in the list of Powers.
@ -239,11 +239,11 @@ ol
.l-main-section .l-main-section
:marked :marked
## Two-way data binding with ***ng-model** ## Two-way data binding with ***ngModel**
Running the app right now would be disappointing. Running the app right now would be disappointing.
figure.image-display figure.image-display
img(src="/resources/images/devguide/forms/hf-3.png" width="400px" alt="Early form with no binding") img(src="/resources/images/devguide/forms/heroForm-3.png" width="400px" alt="Early form with no binding")
:marked :marked
We don't see hero data because we are not binding to the `Hero` yet. We don't see hero data because we are not binding to the `Hero` yet.
We know how to do that from earlier chapters. We know how to do that from earlier chapters.
@ -259,7 +259,7 @@ figure.image-display
Find the `<input>` tag for the "Name" and update it like this Find the `<input>` tag for the "Name" and update it like this
+makeExample('forms/ts/src/app/hero-form.component.html', 'ng-model-1') +makeExample('forms/ts/app/hero-form.component.html', 'ngModel-1','app/hero-form.component.html (excerpt)')(format=".")
.l-sub-section .l-sub-section
:marked :marked
@ -268,42 +268,42 @@ figure.image-display
We left ourselves a note to throw it way when we're done. We left ourselves a note to throw it way when we're done.
:marked :marked
Focus on the binding syntax: `[(ng-model)]="..."`. Focus on the binding syntax: `[(ngModel)]="..."`.
If we ran the app right now and started typing in the *Name* input box, 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 adding and deleting characters, we'd see them appearing and disappearing
from the interpolated text. from the interpolated text.
At some point it might look like this. At some point it might look like this.
figure.image-display figure.image-display
img(src="/resources/images/devguide/forms/ng-model-in-action.png" width="400px" alt="ng-model in action") img(src="/resources/images/devguide/forms/ng-model-in-action.png" width="400px" alt="ngModel in action")
:marked :marked
The diagnostic is evidence that we really are flowing values from the input box to the model and 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!** back again. **That's two-way data binding!**
Let's add similar `[(ng-model)]` bindings to *Alter Ego* and *Hero Power*. Let's add similar `[(ngModel)]` bindings to *Alter Ego* and *Hero Power*.
We'll ditch the input box binding message We'll ditch the input box binding message
and add a new binding at the top to the component's `diagnostic` property. and add a new binding at the top to the component's `diagnostic` property.
Then we can confirm that two-way data binding works *for the entire Hero model*. 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 `[(ng-model)]` bindings that After revision the core of our form should have three `[(ngModel)]` bindings that
look much like this: look much like this:
+makeExample('forms/ts/src/app/hero-form.component.html', 'ng-model-2', 'app/hero-form.component.html (excerpt)') +makeExample('forms/ts/app/hero-form.component.html', 'ngModel-2', 'app/hero-form.component.html (excerpt)')
:marked :marked
If we ran the app right now and changed every Hero model property, the form might display like this: If we ran the app right now and changed every Hero model property, the form might display like this:
figure.image-display figure.image-display
img(src="/resources/images/devguide/forms/ng-model-in-action-2.png" width="400px" alt="ng-model in super action") img(src="/resources/images/devguide/forms/ng-model-in-action-2.png" width="400px" alt="ngModel in super action")
:marked :marked
The diagnostic near the top of the form The diagnostic near the top of the form
confirms that all of our changes are reflected in the model. confirms that all of our changes are reflected in the model.
** We're done with the diagnostic binding. Delete it now.** **Delete** the `{{diagnostic}}` binding at the top as it has served its purpose.
.l-sub-section .l-sub-section
:marked :marked
### Inside [(ng-model)] ### Inside [(ngModel)]
*This section is an optional deep dive into [(ng-model)]. Not interested? Skip ahead!* *This section is an optional deep dive into [(ngModel)]. Not interested? Skip ahead!*
The punctuation in the binding syntax, <span style="font-family:courier"><b>[()]</b></span>, is a good clue to what's going on. The punctuation in the binding syntax, <span style="font-family:courier"><b>[()]</b></span>, is a good clue to what's going on.
@ -320,23 +320,23 @@ figure.image-display
In fact, we can break the `NgModel` binding into its two separate modes In fact, we can break the `NgModel` binding into its two separate modes
as we do in this re-write of the "Name" `<input>` binding: as we do in this re-write of the "Name" `<input>` binding:
+makeExample('forms/ts/src/app/hero-form.component.html', 'ng-model-3') +makeExample('forms/ts/app/hero-form.component.html', 'ngModel-3','app/hero-form.component.html (excerpt)')(format=".")
:marked :marked
<br>The Property Binding should feel familiar. The Event Binding might seem strange. <br>The Property Binding should feel familiar. The Event Binding might seem strange.
The `ng-model-change` is not an `<input>` element event. The `ngModelChange` is not an `<input>` element event.
It is actually an event property of the `NgModel` directive. It is actually an event property of the `NgModel` directive.
When Angular sees a binding target in the form <span style="font-family:courier">[(abc)]</span>, When Angular sees a binding target in the form <span style="font-family:courier">[(abc)]</span>,
it expects the `abc` directive to have an `abc` input property and an `abc-change` output property. it expects the `abc` directive to have an `abc` input property and an `abc-change` output property.
The other oddity is the template expression, `model.name = $event`. The other oddity is the template expression, `model.name = $event`.
We're used to seeing an `$event` object coming from a DOM event. We're used to seeing an `$event` object coming from a DOM event.
The `ng-model-change` property doesn't produce a DOM event; it's an Angular `EventEmitter` The `ngModelChange` property doesn't produce a DOM event; it's an Angular `EventEmitter`
property that returns the input box value when it fires &mdash; which is precisely what property that returns the input box value when it fires &mdash; which is precisely what
we should assign to the model's `name' property. we should assign to the model's `name' property.
Nice to know but is it practical? We almost always prefer `[(ng-model)]`. 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 We might split the binding if we had to do something special in
the event handling such as debounce or throttle the key strokes. the event handling such as debounce or throttle the key strokes.
@ -345,7 +345,7 @@ figure.image-display
.l-main-section .l-main-section
:marked :marked
## Track change-state and validity with **ng-control** ## 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. 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. The `NgControl` directive keeps track of control state for us.
@ -365,18 +365,18 @@ figure.image-display
control and make messages appear or disappear. control and make messages appear or disappear.
We'll explore those effects soon. Right now We'll explore those effects soon. Right now
we should **add `ng-control`to all three form controls**, we should **add `ngControl`to all three form controls**,
starting with the *Name* input box starting with the *Name* input box
+makeExample('forms/ts/src/app/hero-form.component.html', 'ng-control-1', 'app/hero-form.component.html (excerpt)') +makeExample('forms/ts/app/hero-form.component.html', 'ngControl-1', 'app/hero-form.component.html (excerpt)')(format=".")
:marked :marked
Be sure to assign a unique name to each `ng-control` directive. Be sure to assign a unique name to each `ngControl` directive.
.l-sub-section .l-sub-section
:marked :marked
Angular registers controls under their `ng-control` names Angular registers controls under their `ngControl` names
with the `NgForm`. with the `NgForm`.
We didn't add the `NgForm` directive explicitly but it's here We didn't add the `NgForm` directive explicitly but it's here
and we'll talk about it [later in this chapter](#ng-form). and we'll talk about it [later in this chapter](#ngForm).
.l-main-section .l-main-section
:marked :marked
@ -406,7 +406,7 @@ table
Let's add a temporary [local template variable](./template-syntax.html#local-vars) named **spy** Let's add a temporary [local template variable](./template-syntax.html#local-vars) named **spy**
to the "Name" `<input>` tag and use the spy to display those classes. to the "Name" `<input>` tag and use the spy to display those classes.
+makeExample('forms/ts/src/app/hero-form.component.html', 'ng-control-2') +makeExample('forms/ts/app/hero-form.component.html', 'ngControl-2','app/hero-form.component.html (excerpt)')(format=".")
:marked :marked
Now run the app and focus on the *Name* input box. Now run the app and focus on the *Name* input box.
@ -438,12 +438,12 @@ figure.image-display
We achieve this effect by adding two styles to a new `styles.css` file We achieve this effect by adding two styles to a new `styles.css` file
that we add to our project as a sibling to `index.html`. that we add to our project as a sibling to `index.html`.
+makeExample('forms/ts/src/styles.css',null,'styles.css') +makeExample('forms/ts/styles.css',null,'styles.css')(format=".")
:marked :marked
These styles select for the two Angular validity classes and the HTML 5 "required" attribute. These styles select for the two Angular validity classes and the HTML 5 "required" attribute.
We update the `<head>` of the `index.html` to include this style sheet. We update the `<head>` of the `index.html` to include this style sheet.
+makeExample('forms/ts/src/index.html', 'styles', 'index.html (excerpt)')(format=".") +makeExample('forms/ts/index.html', 'styles', 'index.html (excerpt)')(format=".")
:marked :marked
## Show and Hide Validation Error messages ## Show and Hide Validation Error messages
@ -464,39 +464,36 @@ figure.image-display
Here's how we do it for the *name* input box: Here's how we do it for the *name* input box:
-var stylePattern = { otl: /(#name=&quot;form&quot;)|(.*div.*$)|(Name is required)/gm }; -var stylePattern = { otl: /(#name=&quot;form&quot;)|(.*div.*$)|(Name is required)/gm };
+makeExample('forms/ts/src/app/hero-form.component.html', +makeExample('forms/ts/app/hero-form.component.html',
'name-with-error-msg', 'name-with-error-msg',
'app/hero-form.component.html (excerpt)', 'app/hero-form.component.html (excerpt)',
stylePattern) stylePattern)
:marked :marked
We initialized the template local variable with the word "form" (`#name="form"`) When we added the `ngControl` directive, we bound it to the the model's `name` property.
Angular recognizes that syntax and sets the `name` variable Here we initialize a template local variable (`name`) with the value "ngForm" (`#name="ngForm"`).
to the `Control` object identified by the `ng-control` directive which, Angular recognizes that syntax and re-sets the `name` local template variable to the
not coincidentally, we called "name". `ngControl` directive instance.
In other words, the `name` local template variable becomes a handle on the `ngControl` object
for this input box.
We bind the `Control` object's `valid` property to the element's `hidden` property. Now we can control visibility of the "name" error message by binding the message `<div>` element's `hidden` property
While the control is valid, the message is hidden; to the `ngControl` object's `valid` property. The message is hidden while the control is valid;
if it becomes invalid, the message is revealed. the message is revealed when the control becomes invalid.
<a id="ng-form"></a> <a id="ngForm"></a>
.l-sub-section .l-sub-section
:marked :marked
### The NgForm directive ### The NgForm directive
Recall from the previous section that `ng-control` registered this input box with the We just set a template local variable with the value of an `NgForm` directive.
`NgForm` directive as "name". Why did that work? We didn't add the **[`NgForm`](../api/core/NgForm-class.html) directive** explicitly.
We didn't add the **[`NgForm`](../api/core/NgForm-class.html) directive** explicitly.
Angular added it surreptiously, wrapping it around the `<form>` element Angular added it surreptiously, wrapping it around the `<form>` element
The `NgForm` directive supplements the `form` element with additional features. The `NgForm` directive supplements the `form` element with additional features.
It collects `Controls` (elements identified by an `ng-control` directive) It collects `Controls` (elements identified by an `ngControl` directive)
and monitors their properties including their validity. and monitors their properties including their validity.
It has its own `valid` property which is true only if every contained It also has its own `valid` property which is true only if every contained
control is valid. control is valid.
In this example, we are pulling the "name" control out of its `controls` collection
and assigning it to the template local variable so that we can
access the control's properties &mdash; such as the control's own `valid` property.
:marked :marked
The Hero *Alter Ego* is optional so we can leave that be. The Hero *Alter Ego* is optional so we can leave that be.
@ -507,35 +504,32 @@ figure.image-display
.l-main-section .l-main-section
:marked :marked
## Submit the form with **ng-submit** ## Submit the form with **ngSubmit**
The user should be able to submit this form after filling it in. The user should be able to submit this form after filling it in.
The Submit button at the bottom of the form The Submit button at the bottom of the form
does nothing on its own but it will does nothing on its own but it will
trigger a form submit because of its type (`type="submit"`). trigger a form submit because of its type (`type="submit"`).
A "form submit" is meaningless at the moment. A "form submit" is useless at the moment.
To make it meaningful, we'll update the `<form>` tag with another Angular directive, `NgSubmit`, To make it useful, we'll update the `<form>` tag with another Angular directive, `NgSubmit`,
and bind it to our `HeroFormComponent.submit()` method with an EventBinding and bind it to the `HeroFormComponent.submit()` method with an event binding
+makeExample('forms/ts/src/app/hero-form.component.html', 'ng-submit')(format=".") +makeExample('forms/ts/app/hero-form.component.html', 'ngSubmit')(format=".")
:marked :marked
We slipped in something extra there at the end! We defined a We slipped in something extra there at the end! We defined a
template local variable, **`#hf`**, and initialized it with the value, "form". template local variable, **`#heroForm`**, and initialized it with the value, "ngForm".
The variable `hf` is now a handle to the `NgForm` as we [discussed earlier](#ng-form) The variable `heroForm` is now a handle to the `NgForm` directive that we [discussed earlier](#ngForm)
with respect to `ng-control` although this time we have a reference to the form This time `heroForm` remains a reference to the form as a whole.
rather than a control.
We'll bind the Form's over-all validity via Later in the template we bind the button's `disabled` property to the form's over-all validity via
the `hf` variable to the button's `disabled` property the `heroForm` variable. Here's that bit of markup:
using an Event Binding. Here's the code: +makeExample('forms/ts/app/hero-form.component.html', 'submit-button')
+makeExample('forms/ts/src/app/hero-form.component.html', 'submit-button')
:marked :marked
If we run the application now, we find that the button is enabled. Re-run the application. The form opens in a valid state and the button is enabled.
It doesn't do anything useful yet but it's alive.
Now if we delete the *Name*, we violate the "required" rule which Now delete the *Name*. We violate the "name required" rule which
is duely noted in our error message. The Submit button is also disabled. is duely noted in our error message as before. And now the Submit button is also disabled.
Not impressed? Think about it for a moment. What would we have to do to Not impressed? Think about it for a moment. What would we have to do to
wire the button's enable/disabled state to the form's validity without Angular's help? wire the button's enable/disabled state to the form's validity without Angular's help?
@ -563,14 +557,14 @@ figure.image-display
Start by wrapping the form in a `<div>` and bind Start by wrapping the form in a `<div>` and bind
its `hidden` property to the `HeroFormComponent.submitted` property. its `hidden` property to the `HeroFormComponent.submitted` property.
+makeExample('forms/ts/src/app/hero-form.component.html', 'edit-div', 'app/hero-form.component.html (excerpt)') +makeExample('forms/ts/app/hero-form.component.html', 'edit-div', 'app/hero-form.component.html (excerpt)')(format=".")
:marked :marked
The main form is visible from the start because the The main form is visible from the start because the
the `submitted` property is false until we submit the form, the `submitted` property is false until we submit the form,
as this fragment from the `HeroFormComponent` reminds us: as this fragment from the `HeroFormComponent` reminds us:
+makeExample('forms/ts/src/app/hero-form.component.ts', 'submitted') +makeExample('forms/ts/app/hero-form.component.ts', 'submitted')(format=".")
:marked :marked
When we click the Submit button, the `submitted` flag becomes true and the form disappears When we click the Submit button, the `submitted` flag becomes true and the form disappears
@ -578,7 +572,7 @@ figure.image-display
Now we need to show something else while the form is in the submitted state. Now we need to show something else while the form is in the submitted state.
Add the following block of HTML below the `<div>` wrapper we just wrote: Add the following block of HTML below the `<div>` wrapper we just wrote:
+makeExample('forms/ts/src/app/hero-form.component.html', 'submitted', 'app/hero-form.component.html (excerpt)') +makeExample('forms/ts/app/hero-form.component.html', 'submitted', 'app/hero-form.component.html (excerpt)')
:marked :marked
There's our hero again, displayed read-only with interpolation bindings. There's our hero again, displayed read-only with interpolation bindings.
@ -599,43 +593,45 @@ figure.image-display
- An Angular HTML form template. - An Angular HTML form template.
- A form component class with a `Component` decorator. - A form component class with a `Component` decorator.
- The `ng-submit` directive for handling the form submission. - The `ngSubmit` directive for handling the form submission.
- Template local variables such as `#hf`, `#name`, `#alter-ego` and `#power`. - Template local variables such as `#heroForm`, `#name`, `#alter-ego` and `#power`.
- The `ng-model` directive for two-way data binding. - The `ngModel` directive for two-way data binding.
- The `ng-control` for validation and form element change tracking. - The `ngControl` for validation and form element change tracking.
- The local variables `valid` property on input controls to check if a control is valid and show/hide error messages. - The local variables `valid` property on input controls to check if a control is valid and show/hide error messages.
- Property Binding to disable the submit button when the form is invalid. - Controlling the submit button's enabled state by binding to `NgForm` validity.
- Custom CSS classes that provide visual feedback to users about required invalid controls. - Custom CSS classes that provide visual feedback to users about invalid controls.
Heres the final version of the application: Our final project folder structure should look like this:
code-example(format="").
angular2-forms
├── node_modules
├── app
| ├── app.component.ts
| ├── boot.ts
| ├── hero.ts
| ├── hero-form.component.html
| └── hero-form.component.ts
├── index.html
├── styles.css
├── tsconfig.json
└── package.json
:marked
Heres the final version of the source:
+makeTabs( +makeTabs(
`forms/ts/src/app/hero-form.component.html, `forms/ts/app/hero-form.component.ts,
forms/ts/src/app/hero-form.component.ts, forms/ts/app/hero-form.component.html,
forms/ts/src/app/hero.ts, forms/ts/app/hero.ts,
forms/ts/src/app/app.ts, forms/ts/app/app.component.ts,
forms/ts/src/index.html, forms/ts/app/boot.ts,
forms/ts/src/styles.css`, forms/ts/index.html,
'final, final,,,,', forms/ts/styles.css`,
`hero-form.component.html, 'final, final,,,,,',
hero-form.component.ts, `hero-form.component.ts,
hero-form.component.html,
hero.ts, hero.ts,
app.ts, app.component.ts,
boot.ts,
index.html, index.html,
styles.css`) styles.css`)
:marked :marked
Our final project folder structure should look like this:
```
angular2-forms
├── node_modules
├── src
│ ├── app
│ | ├── app.ts
│ | ├── hero.ts
│ | ├── hero-form.component.html
│ | └── hero-form.component.ts
│ ├── index.html
│ ├── styles.css
│ └── tsconfig.json
└── package.json
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB