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
14 changed files with 187 additions and 189 deletions

View File

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

View File

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

View File

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

View File

@ -4,19 +4,12 @@
"description": "",
"main": "index.js",
"scripts": {
"tsc": "tsc -p src -w",
"start": "live-server --open=src"
"tsc": "tsc",
"tsc:w": "tsc -w",
"lite": "lite-server",
"both": "concurrent \"npm run tsc:w\" \"npm run lite\" "
},
"keywords": [],
"author": "",
"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"
}
"license": "ISC"
}

View File

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

View File

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

View File

@ -1,6 +1,5 @@
include ../../../../_includes/_util-fns
<!-- http://plnkr.co/edit/wg154K -->
:marked
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.
@ -20,17 +19,17 @@ include ../../../../_includes/_util-fns
- 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 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
:marked
## Template-Driven Forms
@ -51,7 +50,7 @@ include ../../../../_includes/_util-fns
We'll discuss and learn to build the following template-driven form:
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
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:
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
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 component that controls the form
1. Create a template with the initial form layout
1. Add the **ng-model** directive to each form input control
1. Add the **ng-control** directive to each form input control
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 **ng-submit**
1. Handle form submission with **ngSubmit**
1. Disable the forms submit button until the form is valid
: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:
+makeExample('forms/ts/src/app/hero.ts', null, 'app/hero.ts')
+makeExample('forms/ts/app/hero.ts', null, 'app/hero.ts')
:marked
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?`.
We can create a new hero like this:
```
let myHero = new Hero(42, 'SkyDog', 'Fetch any object at any distance', 'Leslie Rollover');
code-example(format="").
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"
```
:marked
.l-main-section
:marked
@ -124,7 +124,7 @@ figure.image-display
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
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'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
: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:
+makeExample('forms/ts/src/app/app.ts', null, 'app/app.ts')
+makeExample('forms/ts/app/app.component.ts', null, 'app/app.component.ts')
:marked
.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:
+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
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.
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").
npm install bootstrap --save
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
.l-main-section
: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.
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.
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
We are repeating the `<options>` tag for each power in the list of Powers.
@ -239,11 +239,11 @@ ol
.l-main-section
:marked
## Two-way data binding with ***ng-model**
## Two-way data binding with ***ngModel**
Running the app right now would be disappointing.
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
We don't see hero data because we are not binding to the `Hero` yet.
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
+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
:marked
@ -268,42 +268,42 @@ figure.image-display
We left ourselves a note to throw it way when we're done.
: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,
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="ng-model in action")
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 `[(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
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*.
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:
+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
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="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
The diagnostic near the top of the form
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
:marked
### Inside [(ng-model)]
*This section is an optional deep dive into [(ng-model)]. Not interested? Skip ahead!*
### Inside [(ngModel)]
*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.
@ -320,23 +320,23 @@ figure.image-display
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:
+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
<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.
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.
The other oddity is the template expression, `model.name = $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
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
the event handling such as debounce or throttle the key strokes.
@ -345,7 +345,7 @@ figure.image-display
.l-main-section
: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.
The `NgControl` directive keeps track of control state for us.
@ -365,18 +365,18 @@ figure.image-display
control and make messages appear or disappear.
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
+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
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
:marked
Angular registers controls under their `ng-control` names
Angular registers controls under their `ngControl` names
with the `NgForm`.
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
:marked
@ -406,7 +406,7 @@ table
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.
+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
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
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
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.
+makeExample('forms/ts/src/index.html', 'styles', 'index.html (excerpt)')(format=".")
+makeExample('forms/ts/index.html', 'styles', 'index.html (excerpt)')(format=".")
:marked
## Show and Hide Validation Error messages
@ -464,39 +464,36 @@ figure.image-display
Here's how we do it for the *name* input box:
-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',
'app/hero-form.component.html (excerpt)',
stylePattern)
: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.
Here we initialize a template local variable (`name`) with the value "ngForm" (`#name="ngForm"`).
Angular recognizes that syntax and re-sets the `name` local template variable to the
`ngControl` directive instance.
In other words, the `name` local template variable becomes a handle on the `ngControl` object
for this input box.
Angular recognizes that syntax and sets the `name` variable
to the `Control` object identified by the `ng-control` directive which,
not coincidentally, we called "name".
We bind the `Control` object's `valid` property to the element's `hidden` property.
While the control is valid, the message is hidden;
if it becomes invalid, the message is revealed.
<a id="ng-form"></a>
Now we can control visibility of the "name" error message by binding the message `<div>` element's `hidden` property
to the `ngControl` object's `valid` property. The message is hidden while the control is valid;
the message is revealed when the control becomes invalid.
<a id="ngForm"></a>
.l-sub-section
:marked
### The NgForm directive
Recall from the previous section that `ng-control` registered this input box with the
`NgForm` directive as "name".
We didn't add the **[`NgForm`](../api/core/NgForm-class.html) directive** explicitly.
We just set a template local variable with the value of an `NgForm` directive.
Why did that work? We didn't add the **[`NgForm`](../api/core/NgForm-class.html) directive** explicitly.
Angular added it surreptiously, wrapping it around the `<form>` element
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.
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.
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
The Hero *Alter Ego* is optional so we can leave that be.
@ -507,35 +504,32 @@ figure.image-display
.l-main-section
: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 Submit button at the bottom of the form
does nothing on its own but it will
trigger a form submit because of its type (`type="submit"`).
A "form submit" is meaningless at the moment.
To make it meaningful, we'll update the `<form>` tag with another Angular directive, `NgSubmit`,
and bind it to our `HeroFormComponent.submit()` method with an EventBinding
+makeExample('forms/ts/src/app/hero-form.component.html', 'ng-submit')(format=".")
A "form submit" is useless at the moment.
To make it useful, we'll update the `<form>` tag with another Angular directive, `NgSubmit`,
and bind it to the `HeroFormComponent.submit()` method with an event binding
+makeExample('forms/ts/app/hero-form.component.html', 'ngSubmit')(format=".")
:marked
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)
with respect to `ng-control` although this time we have a reference to the form
rather than a control.
The variable `heroForm` is now a handle to the `NgForm` directive that we [discussed earlier](#ngForm)
This time `heroForm` remains a reference to the form as a whole.
We'll bind the Form's over-all validity via
the `hf` variable to the button's `disabled` property
using an Event Binding. Here's the code:
+makeExample('forms/ts/src/app/hero-form.component.html', 'submit-button')
Later in the template we bind the button's `disabled` property to the form's over-all validity via
the `heroForm` variable. Here's that bit of markup:
+makeExample('forms/ts/app/hero-form.component.html', 'submit-button')
:marked
If we run the application now, we find that the button is enabled.
It doesn't do anything useful yet but it's alive.
Re-run the application. The form opens in a valid state and the button is enabled.
Now if we delete the *Name*, we violate the "required" rule which
is duely noted in our error message. The Submit button is also disabled.
Now delete the *Name*. We violate the "name required" rule which
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
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
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
The main form is visible from the start because the
the `submitted` property is false until we submit the form,
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
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.
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
There's our hero again, displayed read-only with interpolation bindings.
@ -599,43 +593,45 @@ figure.image-display
- An Angular HTML form template.
- A form component class with a `Component` decorator.
- The `ng-submit` directive for handling the form submission.
- Template local variables such as `#hf`, `#name`, `#alter-ego` and `#power`.
- The `ng-model` directive for two-way data binding.
- The `ng-control` for validation and form element change tracking.
- The `ngSubmit` directive for handling the form submission.
- Template local variables such as `#heroForm`, `#name`, `#alter-ego` and `#power`.
- The `ngModel` directive for two-way data binding.
- 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.
- Property Binding to disable the submit button when the form is invalid.
- Custom CSS classes that provide visual feedback to users about required invalid controls.
- Controlling the submit button's enabled state by binding to `NgForm` validity.
- Custom CSS classes that provide visual feedback to users about invalid controls.
Heres the final version of the application:
+makeTabs(
`forms/ts/src/app/hero-form.component.html,
forms/ts/src/app/hero-form.component.ts,
forms/ts/src/app/hero.ts,
forms/ts/src/app/app.ts,
forms/ts/src/index.html,
forms/ts/src/styles.css`,
'final, final,,,,',
`hero-form.component.html,
hero-form.component.ts,
hero.ts,
app.ts,
index.html,
styles.css`)
:marked
Our final project folder structure should look like this:
```
code-example(format="").
angular2-forms
├── node_modules
├── src
│ ├── app
│ | ├── app.ts
| ├── hero.ts
| ├── hero-form.component.html
| └── hero-form.component.ts
├── index.html
├── styles.css
│ └── tsconfig.json
├── 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(
`forms/ts/app/hero-form.component.ts,
forms/ts/app/hero-form.component.html,
forms/ts/app/hero.ts,
forms/ts/app/app.component.ts,
forms/ts/app/boot.ts,
forms/ts/index.html,
forms/ts/styles.css`,
'final, final,,,,,',
`hero-form.component.ts,
hero-form.component.html,
hero.ts,
app.component.ts,
boot.ts,
index.html,
styles.css`)
:marked

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB