@@ -21,15 +21,15 @@ include ../_util-fns
21
21
22
22
- two-way data bind with `[(ngModel)]` syntax for reading and writing values to input controls
23
23
24
- - track the change state and validity of form controls using `ngModel` in combination with a form
24
+ - track the change state and validity of form controls using `ngModel` in combination with a form
25
25
26
26
- provide strong visual feedback using special CSS classes that track the state of the controls
27
27
28
28
- display validation errors to users and enable/disable form controls
29
29
30
30
- use [template reference variables](./template-syntax.html#ref-vars) for sharing information among HTML elements
31
-
32
- <live-example>Live Example </live-example>
31
+
32
+ Run the <live-example></live-example>.
33
33
34
34
.l-main-section
35
35
:marked
@@ -107,13 +107,13 @@ include ../_quickstart_repo
107
107
108
108
The TypeScript compiler generates a public field for each `public` constructor parameter and
109
109
assigns the parameter’s value to that field automatically when we create new heroes.
110
-
110
+
111
111
The `alterEgo` is optional and the constructor lets us omit it; note the (?) in `alterEgo?`.
112
112
113
113
We can create a new hero like this:
114
114
code-example( format ="" ) .
115
- let myHero = new Hero(42, 'SkyDog',
116
- 'Fetch any object at any distance',
115
+ let myHero = new Hero(42, 'SkyDog',
116
+ 'Fetch any object at any distance',
117
117
'Leslie Rollover');
118
118
console.log('My hero is called ' + myHero.name); // "My hero is called SkyDog"
119
119
:marked
@@ -159,7 +159,7 @@ code-example(format="").
159
159
write (or read) large stretches of HTML and few editors are much help with files that have a mix of HTML and code.
160
160
We also like short files with a clear and obvious purpose like this one.
161
161
162
- We made a good choice to put the HTML template elsewhere.
162
+ We made a good choice to put the HTML template elsewhere.
163
163
We'll write that template in a moment. Before we do, we'll take a step back
164
164
and revise the `app.module.ts` and `app.component.ts` to make use of our new `HeroFormComponent`.
165
165
@@ -191,7 +191,7 @@ code-example(format="").
191
191
192
192
.alert.is-important
193
193
:marked
194
- If a component, directive, or pipe belongs to a module in the `imports` array, _DON'T_ declare it in the `declarations` array.
194
+ If a component, directive, or pipe belongs to a module in the `imports` array, _DON'T_ declare it in the `declarations` array.
195
195
If you wrote it and it should belong to this module, _DO_ declare it in the `declarations` array.
196
196
197
197
.l-main-section
@@ -230,7 +230,7 @@ code-example(format="").
230
230
231
231
**We are not using Angular yet**. There are no bindings. No extra directives. Just layout.
232
232
233
- The `container`, `form-group`, `form-control`, and `btn` classes
233
+ The `container`, `form-group`, `form-control`, and `btn` classes
234
234
come from [Twitter Bootstrap](http://getbootstrap.com/css/). Purely cosmetic.
235
235
We're using Bootstrap to gussy up our form.
236
236
Hey, what's a form without a little style!
@@ -300,7 +300,7 @@ figure.image-display
300
300
We appended a diagnostic interpolation after the input tag
301
301
so we can see what we're doing.
302
302
We left ourselves a note to throw it away when we're done.
303
-
303
+
304
304
:marked
305
305
Focus on the binding syntax: `[(ngModel)]="..."`.
306
306
@@ -314,13 +314,13 @@ figure.image-display
314
314
The diagnostic is evidence that we really are flowing values from the input box to the model and
315
315
back again. **That's two-way data binding!**
316
316
317
- Notice that we also added a `name` attribute to our `<input>` tag and set it to "name"
317
+ Notice that we also added a `name` attribute to our `<input>` tag and set it to "name"
318
318
which makes sense for the hero's name. Any unique value will do, but using a descriptive name is helpful.
319
319
Defining a `name` attribute is a requirement when using `[(ngModel)]` in combination with a form.
320
320
321
321
.l-sub-section
322
322
:marked
323
- Internally Angular creates `FormControls` and registers them with an `NgForm` directive that Angular
323
+ Internally Angular creates `FormControls` and registers them with an `NgForm` directive that Angular
324
324
attached to the `<form>` tag. Each `FormControl` is registered under the name we assigned to the `name` attribute.
325
325
We'll talk about `NgForm` [later in this chapter](#ngForm).
326
326
@@ -340,7 +340,7 @@ figure.image-display
340
340
- Each input element has an `id` property that is used by the `label` element's `for` attribute
341
341
to match the label to it's input control.
342
342
- Each input element has a `name` property that is required by Angular Forms to register the control with the form.
343
-
343
+
344
344
:marked
345
345
If we ran the app right now and changed every Hero model property, the form might display like this:
346
346
figure.image-display
@@ -355,7 +355,7 @@ figure.image-display
355
355
:marked
356
356
### Inside [(ngModel)]
357
357
*This section is an optional deep dive into [(ngModel)]. Not interested? Skip ahead!*
358
-
358
+
359
359
The punctuation in the binding syntax, <span style="font-family:courier"><b>[()]</b></span>, is a good clue to what's going on.
360
360
361
361
In a Property Binding, a value flows from the model to a target property on screen.
@@ -405,7 +405,7 @@ figure.image-display
405
405
The *NgModel* directive doesn't just track state; it updates the control with special Angular CSS classes that reflect the state.
406
406
We can leverage those class names to change the appearance of the
407
407
control and make messages appear or disappear.
408
-
408
+
409
409
table
410
410
tr
411
411
th State
@@ -491,32 +491,32 @@ figure.image-display
491
491
1. the "*is required*" message in a nearby `<div>` which we'll display only if the control is invalid.
492
492
493
493
Here's how we do it for the *name* input box:
494
- + makeExample('forms/ts/app/hero-form.component.html' ,
495
- 'name-with-error-msg' ,
494
+ + makeExample('forms/ts/app/hero-form.component.html' ,
495
+ 'name-with-error-msg' ,
496
496
'app/hero-form.component.html (excerpt)' )( format ="." )
497
497
:marked
498
498
We need a template reference variable to access the input box's Angular control from within the template.
499
499
Here we created a variable called `name` and gave it the value "ngModel".
500
500
.l-sub-section
501
501
:marked
502
- Why "ngModel"?
502
+ Why "ngModel"?
503
503
A directive's [exportAs](../api/core/index/DirectiveMetadata-class.html#!#exportAs-anchor) property
504
504
tells Angular how to link the reference variable to the directive.
505
505
We set `name` to `ngModel` because the `ngModel` directive's `exportAs` property happens to be "ngModel".
506
506
507
507
Now we can control visibility of the "name" error message by binding properties of the `name` control to the message `<div>` element's `hidden` property.
508
- + makeExample('forms/ts/app/hero-form.component.html' ,
509
- 'hidden-error-msg' ,
508
+ + makeExample('forms/ts/app/hero-form.component.html' ,
509
+ 'hidden-error-msg' ,
510
510
'app/hero-form.component.html (excerpt)' )
511
511
:marked
512
- In this example, we hide the message when the control is valid or pristine;
513
- pristine means the user hasn't changed the value since it was displayed in this form.
514
-
512
+ In this example, we hide the message when the control is valid or pristine;
513
+ pristine means the user hasn't changed the value since it was displayed in this form.
514
+
515
515
This user experience is the developer's choice. Some folks want to see the message at all times.
516
516
If we ignore the `pristine` state, we would hide the message only when the value is valid.
517
- If we arrive in this component with a new (blank) hero or an invalid hero,
517
+ If we arrive in this component with a new (blank) hero or an invalid hero,
518
518
we'll see the error message immediately, before we've done anything.
519
-
519
+
520
520
Some folks find that behavior disconcerting. They only want to see the message when the user makes an invalid change.
521
521
Hiding the message while the control is "pristine" achieves that goal.
522
522
We'll see the significance of this choice when we [add a new hero](#new-hero) to the form.
@@ -527,30 +527,30 @@ figure.image-display
527
527
We can add the same kind of error handling to the `<select>` if we want
528
528
but it's not imperative because the selection box already constrains the
529
529
power to valid value.
530
-
530
+
531
531
<a id =" new-hero" ></a >
532
- <a id =" reset" ></a >
532
+ <a id =" reset" ></a >
533
533
.l-main-section
534
534
:marked
535
535
## Add a hero and reset the form
536
- We'd like to add a new hero in this form.
536
+ We'd like to add a new hero in this form.
537
537
We place a "New Hero" button at the bottom of the form and bind its click event to a component method.
538
- + makeExample('forms/ts/app/hero-form.component.html' ,
539
- 'new-hero-button' ,
538
+ + makeExample('forms/ts/app/hero-form.component.html' ,
539
+ 'new-hero-button' ,
540
540
'app/hero-form.component.html (New Hero button)' )
541
541
:marked
542
- + makeExample('forms/ts/app/hero-form.component.ts' ,
543
- 'new-hero-v1' ,
542
+ + makeExample('forms/ts/app/hero-form.component.ts' ,
543
+ 'new-hero-v1' ,
544
544
'app/hero-form.component.ts (New Hero method - v1)' )( format ="." )
545
545
:marked
546
546
Run the application again, click the *New Hero* button, and the form clears.
547
547
The *required* bars to the left of the input box are red, indicating invalid `name` and `power` properties.
548
- That's understandable as these are required fields.
548
+ That's understandable as these are required fields.
549
549
The error messages are hidden because the form is pristine; we haven't changed anything yet.
550
-
550
+
551
551
Enter a name and click *New Hero* again.
552
552
This time we see an error message! Why? We don't want that when we display a new (empty) hero.
553
-
553
+
554
554
Inspecting the element in the browser tools reveals that the *name* input box is no longer pristine.
555
555
Replacing the hero *did not restore the pristine state* of the control.
556
556
.l-sub-section
@@ -559,26 +559,26 @@ figure.image-display
559
559
replacing the entire hero and clearing the `name` property programmatically.
560
560
Angular makes no assumptions and leaves the control in its current, dirty state.
561
561
:marked
562
- We'll have to reset the form controls manually with a small trick.
562
+ We'll have to reset the form controls manually with a small trick.
563
563
We add an `active` flag to the component, initialized to `true`. When we add a new hero,
564
564
we toggle `active` false and then immediately back to true with a quick `setTimeout`.
565
- + makeExample('forms/ts/app/hero-form.component.ts' ,
566
- 'new-hero' ,
565
+ + makeExample('forms/ts/app/hero-form.component.ts' ,
566
+ 'new-hero' ,
567
567
'app/hero-form.component.ts (New Hero method - final)' )( format ="." )
568
568
:marked
569
569
Then we bind the form element to this `active` flag.
570
- + makeExample('forms/ts/app/hero-form.component.html' ,
571
- 'form-active' ,
570
+ + makeExample('forms/ts/app/hero-form.component.html' ,
571
+ 'form-active' ,
572
572
'app/hero-form.component.html (Form tag)' )
573
573
:marked
574
- With `NgIf` bound to the `active` flag,
574
+ With `NgIf` bound to the `active` flag,
575
575
clicking "New Hero" removes the form from the DOM and recreates it in a blink of an eye.
576
576
The re-created form is in a pristine state. The error message is hidden.
577
577
.l-sub-section
578
578
:marked
579
579
This is a temporary workaround while we await a proper form reset feature.
580
580
:marked
581
-
581
+
582
582
.l-main-section
583
583
:marked
584
584
## Submit the form with **ngSubmit**
@@ -602,15 +602,15 @@ figure.image-display
602
602
:marked
603
603
### The NgForm directive
604
604
What `NgForm` directive? We didn't add an [NgForm](../api/common/index/NgForm-directive.html) directive!
605
-
605
+
606
606
Angular did. Angular creates and attaches an `NgForm` directive to the `<form>` tag automatically.
607
607
608
608
The `NgForm` directive supplements the `form` element with additional features.
609
609
It holds the controls we created for the elements with `ngModel` directive and `name` attribute
610
610
and monitors their properties including their validity.
611
611
It also has its own `valid` property which is true only *if every contained
612
612
control* is valid.
613
-
613
+
614
614
:marked
615
615
Later in the template we bind the button's `disabled` property to the form's over-all validity via
616
616
the `heroForm` variable. Here's that bit of markup:
@@ -686,7 +686,7 @@ figure.image-display
686
686
- A form component class with a `Component` decorator.
687
687
- The `ngSubmit` directive for handling the form submission.
688
688
- Template reference variables such as `#heroForm`, `#name` and `#power`.
689
- - The `[(ngModel)]` syntax and a `name` attribute for two-way data binding, validation and change tracking.
689
+ - The `[(ngModel)]` syntax and a `name` attribute for two-way data binding, validation and change tracking.
690
690
- The reference variable’s `valid` property on input controls to check if a control is valid and show/hide error messages.
691
691
- Controlling the submit button's enabled state by binding to `NgForm` validity.
692
692
- Custom CSS classes that provide visual feedback to users about invalid controls.
@@ -704,7 +704,7 @@ figure.image-display
704
704
.file hero-form.component.ts
705
705
.file main.ts
706
706
.file node_modules ...
707
- .file typings ...
707
+ .file typings ...
708
708
.file index.html
709
709
.file package.json
710
710
.file tsconfig.json
@@ -718,12 +718,12 @@ figure.image-display
718
718
forms/ts/app/hero.ts,
719
719
forms/ts/app/app.module.ts,
720
720
forms/ts/app/app.component.ts,
721
- forms/ts/app/main.ts,
721
+ forms/ts/app/main.ts,
722
722
forms/ts/index.html,
723
723
forms/ts/forms.css` ,
724
724
'final, final,,,,,' ,
725
725
` hero-form.component.ts,
726
- hero-form.component.html,
726
+ hero-form.component.html,
727
727
hero.ts,
728
728
app.module.ts,
729
729
app.component.ts,
0 commit comments