docs(forms): clarify NgControlName directive and NgForm
This commit is contained in:
parent
1c6594b3c8
commit
271b64b7e3
|
@ -17,17 +17,17 @@ include ../_util-fns
|
||||||
|
|
||||||
We will build a simple form from scratch, one step at a time. Along the way we'll learn
|
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
|
- 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
|
- two-way data binding with `[(ngModel)]` syntax for reading and writing values to input controls
|
||||||
|
|
||||||
- The `ngControl` directive to track the change state and validity of form controls
|
- using `ngControl` 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
|
- 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
|
- displaying validation errors to users and enable/disable form controls
|
||||||
|
|
||||||
- How to share information across controls with template local variables
|
- sharing information among controls with template local variables
|
||||||
|
|
||||||
[Live Example](/resources/live-examples/forms/ts/plnkr.html)
|
[Live Example](/resources/live-examples/forms/ts/plnkr.html)
|
||||||
.l-main-section
|
.l-main-section
|
||||||
|
@ -75,7 +75,7 @@ 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 **ngModel** directive to each form input control
|
1. Bind data properties to each form input control with the `ngModel` two-way data binding syntax
|
||||||
1. Add the **ngControl** 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
|
||||||
|
@ -240,7 +240,7 @@ ol
|
||||||
<a id="ngModel"></a>
|
<a id="ngModel"></a>
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Two-way data binding with ***ngModel**
|
## 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
|
||||||
|
@ -255,7 +255,7 @@ figure.image-display
|
||||||
Now we need to display, listen, and extract at the same time.
|
Now we need to display, listen, and extract at the same time.
|
||||||
|
|
||||||
We could use those techniques again in our form.
|
We could use those techniques again in our form.
|
||||||
Instead we'll introduce something new, the `NgModel` directive, that
|
Instead we'll introduce something new, the `[(ngModel)]` syntax, that
|
||||||
makes binding our form to the model super-easy.
|
makes binding our form to the model super-easy.
|
||||||
|
|
||||||
Find the `<input>` tag for the "Name" and update it like this
|
Find the `<input>` tag for the "Name" and update it like this
|
||||||
|
@ -349,41 +349,43 @@ figure.image-display
|
||||||
## Track change-state and validity with **ngControl**
|
## 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.
|
|
||||||
|
|
||||||
.callout.is-helpful
|
By setting `ngControl` we create a directive that can tell if the user touched the control,
|
||||||
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 `<form`> tag.
|
|
||||||
:marked
|
|
||||||
Our application can ask an `NgControl` if the user touched the control,
|
|
||||||
if the value changed, or if the value became invalid.
|
if the value changed, or if the value became invalid.
|
||||||
|
|
||||||
`NgControl` doesn't just track state; it updates the control with special
|
This directive doesn't just track state; it updates the control with special
|
||||||
Angular CSS classes from the set we listed above.
|
Angular CSS classes from the set we listed above.
|
||||||
We can leverage those class names to change the appearance of the
|
We can leverage those class names to change the appearance of the
|
||||||
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 `ngControl`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/app/hero-form.component.html', 'ngControl-1', 'app/hero-form.component.html (excerpt)')(format=".")
|
+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 `ngControl` directive.
|
We set this particular `ngControl` to "name" which makes sense for our app. Any unique value will do.
|
||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
Angular registers controls under their `ngControl` names
|
Internally Angular creates `Controls` and registers them under their `ngControl` names
|
||||||
with the `NgForm`.
|
with an `NgForm` directive that Angular attached to the `<form>` tag.
|
||||||
We didn't add the `NgForm` directive explicitly but it's here
|
We'll talk about `NgForm` [later in the chapter](#ngForm).
|
||||||
and we'll talk about it [later in this chapter](#ngForm).
|
|
||||||
|
The `ngControl` *attribute* in our template actually maps to the
|
||||||
|
[NgControlName](../api/common/NgControlName-directive.html) directive.
|
||||||
|
There is also a `NgControl` *abstract* directive which is *not the same thing*.
|
||||||
|
We often ignore this technical distinction and refer to `NgControlName` more conveniently (albeit incorrectly) as the *NgControl* directive.
|
||||||
|
|
||||||
|
While we're under the hood, we might as well note that the `ngModel` in the
|
||||||
|
two-way binding syntax is now a property of the the `NgControlName` directive.
|
||||||
|
The `NgModel` directive is no longer involved. We only need one directive to manage the DOM element
|
||||||
|
and there is no practical difference in the way either directive handles data binding.
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Add Custom CSS for Visual Feedback
|
## Add Custom CSS for Visual Feedback
|
||||||
|
|
||||||
`NgControl` doesn't just track state.
|
The *NgControl* directive doesn't just track state.
|
||||||
It updates the control with three classes that reflect the state.
|
It updates the control with three classes that reflect the state.
|
||||||
|
|
||||||
table
|
table
|
||||||
|
@ -468,13 +470,19 @@ figure.image-display
|
||||||
'name-with-error-msg',
|
'name-with-error-msg',
|
||||||
'app/hero-form.component.html (excerpt)')(format=".")
|
'app/hero-form.component.html (excerpt)')(format=".")
|
||||||
:marked
|
:marked
|
||||||
When we added the `ngControl` directive, we bound it to the the model's `name` property.
|
We need a template local variable to access the input box's Angular control from within the template.
|
||||||
|
Here we created a variable called `name` and gave it the value "ngForm".
|
||||||
Here we initialize a template local variable (`name`) with the value "ngForm" (`#name="ngForm"`).
|
.l-sub-section
|
||||||
Angular recognizes that syntax and re-sets the `name` local template variable to the
|
:marked
|
||||||
`ngControl` directive instance.
|
Why "ngForm"?
|
||||||
In other words, the `name` local template variable becomes a handle on the `ngControl` object
|
A directive's [exportAs](/docs/ts/latest/api/core/DirectiveMetadata-class.html#!#exportAs) property
|
||||||
for this input box.
|
tells Angular how to link local variable to the directive.
|
||||||
|
We set `name` to `ngForm` because the `NgControlName` directive's `exportAs` property happens to be "ngForm".
|
||||||
|
|
||||||
|
This seems unintuitive at first until we realize that *all* control directives in the
|
||||||
|
Angular form family &mdash including `NgForm`, `NgModel`, `NgControlName` and `NgControlGroup` — *exportAs* "ngForm"
|
||||||
|
and we only ever apply *one* of these directives to an element tag.
|
||||||
|
Consistency rules!
|
||||||
|
|
||||||
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.
|
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.
|
||||||
+makeExample('forms/ts/app/hero-form.component.html',
|
+makeExample('forms/ts/app/hero-form.component.html',
|
||||||
|
@ -492,21 +500,7 @@ figure.image-display
|
||||||
Some folks find that behavior disconcerting. They only want to see the message when the user makes an invalid change.
|
Some folks find that behavior disconcerting. They only want to see the message when the user makes an invalid change.
|
||||||
Hiding the message while the control is "pristine" achieves that goal.
|
Hiding the message while the control is "pristine" achieves that goal.
|
||||||
We'll see the significance of this choice when we [add a new hero](#new-hero) to the form.
|
We'll see the significance of this choice when we [add a new hero](#new-hero) to the form.
|
||||||
<a id="ngForm"></a>
|
|
||||||
.l-sub-section
|
|
||||||
:marked
|
|
||||||
### The NgForm directive
|
|
||||||
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 `ngControl` directive)
|
|
||||||
and monitors their properties including their validity.
|
|
||||||
It also has its own `valid` property which is true only if every contained
|
|
||||||
control is valid.
|
|
||||||
: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.
|
||||||
|
|
||||||
Hero *Power* selection is required.
|
Hero *Power* selection is required.
|
||||||
|
@ -582,9 +576,22 @@ figure.image-display
|
||||||
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, **`#heroForm`**, and initialized it with the value, "ngForm".
|
template local variable, **`#heroForm`**, and initialized it with the value, "ngForm".
|
||||||
|
|
||||||
The variable `heroForm` is now a handle to the `NgForm` directive that we [discussed earlier](#ngForm)
|
The variable `heroForm` is now a reference to the `NgForm` directive that governs the form as a whole.
|
||||||
This time `heroForm` remains a reference to the form as a whole.
|
<a id="ngForm"></a>
|
||||||
|
.l-sub-section
|
||||||
|
:marked
|
||||||
|
### The NgForm directive
|
||||||
|
What `NgForm` directive? We didn't add an [NgForm](../api/core/NgForm-class.html) directive!
|
||||||
|
|
||||||
|
Angular did. Angular creates and attaches an `NgForm` directive to the `<form>` tag automatically.
|
||||||
|
|
||||||
|
The `NgForm` directive supplements the `form` element with additional features.
|
||||||
|
It holds the controls we created for the elements with `ngControl` attributes
|
||||||
|
and monitors their properties including their validity.
|
||||||
|
It also has its own `valid` property which is true only *if every contained
|
||||||
|
control* is valid.
|
||||||
|
|
||||||
|
:marked
|
||||||
Later in the template we bind the button's `disabled` property to 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 `heroForm` variable. Here's that bit of markup:
|
the `heroForm` variable. Here's that bit of markup:
|
||||||
+makeExample('forms/ts/app/hero-form.component.html', 'submit-button')
|
+makeExample('forms/ts/app/hero-form.component.html', 'submit-button')
|
||||||
|
@ -658,8 +665,8 @@ figure.image-display
|
||||||
- A form component class with a `Component` decorator.
|
- A form component class with a `Component` decorator.
|
||||||
- The `ngSubmit` directive for handling the form submission.
|
- The `ngSubmit` directive for handling the form submission.
|
||||||
- Template local variables such as `#heroForm`, `#name`, `#alter-ego` and `#power`.
|
- Template local variables such as `#heroForm`, `#name`, `#alter-ego` and `#power`.
|
||||||
- The `ngModel` directive for two-way data binding.
|
- The `[(ngModel)]` syntax for two-way data binding.
|
||||||
- The `ngControl` for validation and form element change tracking.
|
- The `ngControlName` directive for validation and form element change tracking.
|
||||||
- The local variable’s `valid` property on input controls to check if a control is valid and show/hide error messages.
|
- The local variable’s `valid` property on input controls to check if a control is valid and show/hide error messages.
|
||||||
- Controlling the submit button's enabled state by binding to `NgForm` validity.
|
- Controlling the submit button's enabled state by binding to `NgForm` validity.
|
||||||
- Custom CSS classes that provide visual feedback to users about invalid controls.
|
- Custom CSS classes that provide visual feedback to users about invalid controls.
|
||||||
|
|
Loading…
Reference in New Issue