docs(forms): revise prose for form.reset (#2853)
This commit is contained in:
parent
a7525721c0
commit
434308ab1b
|
@ -36,7 +36,7 @@
|
|||
required
|
||||
[(ngModel)]="model.power" name="power"
|
||||
#power="ngModel" >
|
||||
<option *ngFor="let p of powers" [value]="p">{{p}}</option>
|
||||
<option *ngFor="let pow of powers" [value]="pow">{{pow}}</option>
|
||||
</select>
|
||||
<div [hidden]="power.valid || power.pristine" class="alert alert-danger">
|
||||
Power is required
|
||||
|
@ -46,12 +46,18 @@
|
|||
<!-- #docregion submit-button -->
|
||||
<button type="submit" class="btn btn-default" [disabled]="!heroForm.form.valid">Submit</button>
|
||||
<!-- #enddocregion submit-button -->
|
||||
|
||||
<!-- #docregion new-hero-button -->
|
||||
<!-- #docregion new-hero-button-form-reset -->
|
||||
<button type="button" class="btn btn-default" (click)="newHero(); heroForm.reset()">New Hero</button>
|
||||
<!-- #enddocregion new-hero-button -->
|
||||
|
||||
<!-- #enddocregion new-hero-button-form-reset -->
|
||||
<!-- #enddocregion final -->
|
||||
<i>with</i> reset
|
||||
|
||||
|
||||
<!-- #docregion new-hero-button-no-reset -->
|
||||
<button type="button" class="btn btn-default" (click)="newHero()">New Hero</button>
|
||||
<!-- #enddocregion new-hero-button-no-reset -->
|
||||
<i>without</i> reset
|
||||
|
||||
<!-- NOT SHOWN IN DOCS -->
|
||||
<div>
|
||||
<hr>
|
||||
|
@ -126,7 +132,7 @@
|
|||
<div class="form-group">
|
||||
<label for="power">Hero Power</label>
|
||||
<select class="form-control" id="power" required>
|
||||
<option *ngFor="let p of powers" [value]="p">{{p}}</option>
|
||||
<option *ngFor="let pow of powers" [value]="pow">{{pow}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
@ -165,7 +171,7 @@
|
|||
<select class="form-control" id="power"
|
||||
required
|
||||
[(ngModel)]="model.power" name="power">
|
||||
<option *ngFor="let p of powers" [value]="p">{{p}}</option>
|
||||
<option *ngFor="let pow of powers" [value]="pow">{{pow}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
@ -178,12 +184,12 @@
|
|||
|
||||
<!-- EXTRA MATERIAL FOR DOCUMENTATION -->
|
||||
<hr>
|
||||
<!-- #docregion ngModel-1-->
|
||||
<!-- #docregion ngModelName-1 -->
|
||||
<input type="text" class="form-control" id="name"
|
||||
required
|
||||
[(ngModel)]="model.name" name="name">
|
||||
TODO: remove this: {{model.name}}
|
||||
<!-- #enddocregion ngModel-1-->
|
||||
<!-- #enddocregion ngModelName-1 -->
|
||||
<hr>
|
||||
<!-- #docregion ngModel-3-->
|
||||
<input type="text" class="form-control" id="name"
|
||||
|
@ -193,16 +199,6 @@
|
|||
TODO: remove this: {{model.name}}
|
||||
<!-- #enddocregion ngModel-3-->
|
||||
<hr>
|
||||
<!-- #docregion form-reset -->
|
||||
<button type="button" class="btn btn-default" (click)="newHero(); heroForm.reset()">New Hero</button>
|
||||
<!-- #enddocregion form-reset -->
|
||||
|
||||
<!-- #docregion ngModelName-1 -->
|
||||
<input type="text" class="form-control" id="name"
|
||||
required
|
||||
[(ngModel)]="model.name" name="name" >
|
||||
<!-- #enddocregion ngModelName-1 -->
|
||||
<hr>
|
||||
<!-- #docregion ngModelName-2 -->
|
||||
<input type="text" class="form-control" id="name"
|
||||
required
|
||||
|
|
|
@ -30,11 +30,9 @@ export class HeroFormComponent {
|
|||
|
||||
// #docregion final
|
||||
// #docregion new-hero
|
||||
// #docregion new-hero-v1
|
||||
newHero() {
|
||||
this.model = new Hero(42, '', '');
|
||||
}
|
||||
// #enddocregion new-hero-v1
|
||||
// #enddocregion new-hero
|
||||
// #enddocregion final
|
||||
//////// NOT SHOWN IN DOCS ////////
|
||||
|
|
|
@ -2,7 +2,4 @@
|
|||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
// Compiles the module (asynchronously) with the runtime compiler
|
||||
// which generates a compiled module factory in memory.
|
||||
// Then bootstraps with that factory, targeting the browser.
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
|
|
|
@ -9,11 +9,11 @@ include ../_util-fns
|
|||
It's more challenging to create a cohesive data entry experience that guides the
|
||||
user efficiently and effectively through the workflow behind the form.
|
||||
|
||||
*That* takes design skills that are, to be frank, well out of scope for this chapter.
|
||||
*That* takes design skills that are, to be frank, well out of scope for this guide.
|
||||
|
||||
It also takes framework support for
|
||||
**two-way data binding, change tracking, validation, and error handling**
|
||||
... which we shall cover in this chapter on Angular forms.
|
||||
... which we shall cover in this guide on Angular forms.
|
||||
|
||||
We will build a simple form from scratch, one step at a time. Along the way we'll learn how to
|
||||
|
||||
|
@ -36,10 +36,10 @@ include ../_util-fns
|
|||
## Template-Driven Forms
|
||||
|
||||
Many of us will build forms by writing templates in the Angular [template syntax](./template-syntax.html) with
|
||||
the form-specific directives and techniques described in this chapter.
|
||||
the form-specific directives and techniques described in this guide.
|
||||
.l-sub-section
|
||||
:marked
|
||||
That's not the only way to create a form but it's the way we'll cover in this chapter.
|
||||
That's not the only way to create a form but it's the way we'll cover in this guide.
|
||||
:marked
|
||||
We can build almost any form we need with an Angular template — login forms, contact forms ... pretty much any business forms.
|
||||
We can lay out the controls creatively, bind them to data, specify validation rules and display validation errors,
|
||||
|
@ -122,9 +122,9 @@ code-example(format="").
|
|||
:marked
|
||||
## Create a Form component
|
||||
|
||||
An Angular form has two parts: an HTML-based template and a code-based Component to handle data and user interactions.
|
||||
|
||||
We begin with the Component because it states, in brief, what the Hero editor can do.
|
||||
An Angular form has two parts: an HTML-based _template_ and a component _class_
|
||||
to handle data and user interactions programmatically.
|
||||
We begin with the class because it states, in brief, what the hero editor can do.
|
||||
|
||||
Create a new file called `hero-form.component.ts` and give it the following definition:
|
||||
|
||||
|
@ -133,7 +133,7 @@ code-example(format="").
|
|||
:marked
|
||||
There’s nothing special about this component, nothing form-specific, nothing to distinguish it from any component we've written before.
|
||||
|
||||
Understanding this component requires only the Angular concepts we’ve learned in previous chapters
|
||||
Understanding this component requires only the Angular concepts we’ve learned in previous guides
|
||||
|
||||
1. We import the `Component` decorator from the Angular library as we usually do.
|
||||
|
||||
|
@ -153,17 +153,19 @@ code-example(format="").
|
|||
1. We threw in a `diagnostic` property at the end to return a JSON representation of our model.
|
||||
It'll help us see what we're doing during our development; we've left ourselves a cleanup note to discard it later.
|
||||
|
||||
Why don't we write the template inline in the component file as we often do
|
||||
elsewhere in the Developer Guide?
|
||||
### Why the separate template file?
|
||||
|
||||
Why don't we write the template inline in the component file as we often do elsewhere?
|
||||
|
||||
There is no “right” answer for all occasions. We like inline templates when they are short.
|
||||
Most form templates won't be short. TypeScript and JavaScript files generally aren't the best place to
|
||||
write (or read) large stretches of HTML and few editors are much help with files that have a mix of HTML and code.
|
||||
We also like short files with a clear and obvious purpose like this one.
|
||||
|
||||
We made a good choice to put the HTML template elsewhere.
|
||||
We'll write that template in a moment. Before we do, we'll take a step back
|
||||
and revise the `app.module.ts` and `app.component.ts` to make use of our new `HeroFormComponent`.
|
||||
Form templates tend to be quite large even when displaying a small number of fields
|
||||
so it's usually best to put the HTML template in a separate file.
|
||||
We'll write that template file in a moment. Before we do, we'll take a step back
|
||||
and revise the `app.module.ts` and `app.component.ts` to make use of the new `HeroFormComponent`.
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
|
@ -193,7 +195,7 @@ code-example(format="").
|
|||
|
||||
.alert.is-important
|
||||
:marked
|
||||
If a component, directive, or pipe belongs to a module in the `imports` array, _DON'T_ declare it in the `declarations` array.
|
||||
If a component, directive, or pipe belongs to a module in the `imports` array, _DON'T_ re-declare it in the `declarations` array.
|
||||
If you wrote it and it should belong to this module, _DO_ declare it in the `declarations` array.
|
||||
|
||||
.l-main-section
|
||||
|
@ -208,9 +210,8 @@ code-example(format="").
|
|||
:marked
|
||||
.l-sub-section
|
||||
:marked
|
||||
There is only one change:
|
||||
|
||||
1. The `template` is simply the new element tag identified by the component's `selector` property.
|
||||
There is only one change.
|
||||
The `template` is simply the new element tag identified by the component's `selector` property.
|
||||
This will display the hero form when the application component is loaded.
|
||||
|
||||
.l-main-section
|
||||
|
@ -234,8 +235,7 @@ code-example(format="").
|
|||
|
||||
The `container`, `form-group`, `form-control`, and `btn` classes
|
||||
come from [Twitter Bootstrap](http://getbootstrap.com/css/). Purely cosmetic.
|
||||
We're using Bootstrap to gussy up our form.
|
||||
Hey, what's a form without a little style!
|
||||
We're using Bootstrap to give the form a little style!
|
||||
|
||||
.callout.is-important
|
||||
header Angular Forms Do Not Require A Style Library
|
||||
|
@ -262,27 +262,27 @@ ol
|
|||
|
||||
We'll add a `select` to our
|
||||
form and bind the options to the `powers` list using `ngFor`,
|
||||
a technique we might have seen before in the [Displaying Data](./displaying-data.html) chapter.
|
||||
a technique seen previously in the [Displaying Data](./displaying-data.html) guide.
|
||||
|
||||
Add the following HTML *immediately below* the *Alter Ego* group.
|
||||
+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.
|
||||
The `p` template input variable is a different power in each iteration;
|
||||
The `pow` template input variable is a different power in each iteration;
|
||||
we display its name using the interpolation syntax with the double-curly-braces.
|
||||
|
||||
<a id="ngModel"></a>
|
||||
.l-main-section
|
||||
:marked
|
||||
## Two-way data binding with **ngModel**
|
||||
## Two-way data binding with **_ngModel_**
|
||||
Running the app right now would be disappointing.
|
||||
|
||||
figure.image-display
|
||||
img(src="/resources/images/devguide/forms/hero-form-3.png" width="400px" alt="Early form with no binding")
|
||||
:marked
|
||||
We don't see hero data because we are not binding to the `Hero` yet.
|
||||
We know how to do that from earlier chapters.
|
||||
We know how to do that from earlier guides.
|
||||
[Displaying Data](./displaying-data.html) taught us Property Binding.
|
||||
[User Input](./user-input.html) showed us how to listen for DOM events with an
|
||||
Event Binding and how to update a component property with the displayed value.
|
||||
|
@ -295,7 +295,7 @@ figure.image-display
|
|||
|
||||
Find the `<input>` tag for the "Name" and update it like this
|
||||
|
||||
+makeExample('forms/ts/app/hero-form.component.html', 'ngModel-1','app/hero-form.component.html (excerpt)')(format=".")
|
||||
+makeExample('forms/ts/app/hero-form.component.html', 'ngModelName-1','app/hero-form.component.html (excerpt)')(format=".")
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
|
@ -324,13 +324,13 @@ figure.image-display
|
|||
:marked
|
||||
Internally Angular creates `FormControls` and registers them with an `NgForm` directive that Angular
|
||||
attached to the `<form>` tag. Each `FormControl` is registered under the name we assigned to the `name` attribute.
|
||||
We'll talk about `NgForm` [later in this chapter](#ngForm).
|
||||
We'll talk about `NgForm` [later in this guide](#ngForm).
|
||||
|
||||
:marked
|
||||
Let's add similar `[(ngModel)]` bindings and `name` attributes 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*.
|
||||
Then we can confirm that two-way data binding works *for the entire hero model*.
|
||||
|
||||
After revision the core of our form should have three `[(ngModel)]` bindings and `name` attributes that
|
||||
look much like this:
|
||||
|
@ -344,7 +344,7 @@ figure.image-display
|
|||
- Each input element has a `name` property that is required by Angular Forms to register the control with the form.
|
||||
|
||||
: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
|
||||
img(src="/resources/images/devguide/forms/ng-model-in-action-2.png" width="400px" alt="ngModel in super action")
|
||||
:marked
|
||||
|
@ -355,7 +355,7 @@ figure.image-display
|
|||
|
||||
.l-sub-section
|
||||
:marked
|
||||
### Inside [(ngModel)]
|
||||
### 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.
|
||||
|
@ -394,11 +394,11 @@ figure.image-display
|
|||
the event handling such as debounce or throttle the key strokes.
|
||||
|
||||
Learn more about `NgModel` and other template syntax in the
|
||||
[Template Syntax](./template-syntax.html) chapter.
|
||||
[Template Syntax](./template-syntax.html) guide.
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## Track change-state and validity with **ngModel**
|
||||
## Track change-state and validity with **_ngModel_**
|
||||
|
||||
A form isn't just about data binding. We'd also like to know the state of the controls on our form.
|
||||
|
||||
|
@ -509,7 +509,7 @@ figure.image-display
|
|||
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',
|
||||
'hidden-error-msg',
|
||||
'app/hero-form.component.html (excerpt)')
|
||||
'app/hero-form.component.html (excerpt)')(format='.')
|
||||
:marked
|
||||
In this example, we hide the message when the control is valid or pristine;
|
||||
pristine means the user hasn't changed the value since it was displayed in this form.
|
||||
|
@ -523,7 +523,7 @@ figure.image-display
|
|||
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.
|
||||
|
||||
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.
|
||||
We can add the same kind of error handling to the `<select>` if we want
|
||||
|
@ -536,14 +536,14 @@ figure.image-display
|
|||
:marked
|
||||
## Add a hero and reset the form
|
||||
We'd like to add a new hero in this form.
|
||||
We place a "New Hero" button at the bottom of the form and bind its click event to a component method.
|
||||
We place a "New Hero" button at the bottom of the form and bind its click event to a `newHero` component method.
|
||||
+makeExample('forms/ts/app/hero-form.component.html',
|
||||
'new-hero-button',
|
||||
'new-hero-button-no-reset',
|
||||
'app/hero-form.component.html (New Hero button)')
|
||||
:marked
|
||||
+makeExample('forms/ts/app/hero-form.component.ts',
|
||||
'new-hero-v1',
|
||||
'app/hero-form.component.ts (New Hero method - v1)')(format=".")
|
||||
'new-hero',
|
||||
'app/hero-form.component.ts (New Hero method)')(format=".")
|
||||
:marked
|
||||
Run the application again, click the *New Hero* button, and the form clears.
|
||||
The *required* bars to the left of the input box are red, indicating invalid `name` and `power` properties.
|
||||
|
@ -551,28 +551,26 @@ figure.image-display
|
|||
The error messages are hidden because the form is pristine; we haven't changed anything yet.
|
||||
|
||||
Enter a name and click *New Hero* again.
|
||||
This time we see an error message! Why? We don't want that when we display a new (empty) hero.
|
||||
The app displays a **_Name is required_** error message!
|
||||
We don't want error messages when we create a new (empty) hero.
|
||||
Why are we getting one now?
|
||||
|
||||
Inspecting the element in the browser tools reveals that the *name* input box is no longer pristine.
|
||||
Replacing the hero *did not restore the pristine state* of the control.
|
||||
.l-sub-section
|
||||
:marked
|
||||
Upon reflection, we realize that Angular cannot distinguish between
|
||||
replacing the entire hero and clearing the `name` property programmatically.
|
||||
Angular makes no assumptions and leaves the control in its current, dirty state.
|
||||
:marked
|
||||
We'll have to reset the form controls.
|
||||
We call the `reset()` method of the form after calling the `newHero()` method.
|
||||
Inspecting the element in the browser tools reveals that the *name* input box is _no longer pristine_.
|
||||
The form remembers that we entered a name before clicking *New Hero*.
|
||||
Replacing the hero object *did not restore the pristine state* of the form controls.
|
||||
|
||||
We have to clear all of the flags imperatively which we can do
|
||||
by calling the form's `reset()` method after calling the `newHero()` method.
|
||||
+makeExample('forms/ts/app/hero-form.component.html',
|
||||
'form-reset',
|
||||
'new-hero-button-form-reset',
|
||||
'app/hero-form.component.html (Reset the form)')
|
||||
:marked
|
||||
This will reset the `heroForm` and its status by clicking "New Hero".
|
||||
Now clicking "New Hero" both resets the form and its control flags.
|
||||
:marked
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## Submit the form with **ngSubmit**
|
||||
## 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
|
||||
|
@ -591,7 +589,7 @@ figure.image-display
|
|||
<a id="ngForm"></a>
|
||||
.l-sub-section
|
||||
:marked
|
||||
### The NgForm directive
|
||||
### The _NgForm_ directive
|
||||
What `NgForm` directive? We didn't add an [NgForm](../api/forms/index/NgForm-directive.html) directive!
|
||||
|
||||
Angular did. Angular creates and attaches an `NgForm` directive to the `<form>` tag automatically.
|
||||
|
@ -629,7 +627,7 @@ figure.image-display
|
|||
jazzing it up won't teach us anything new about forms.
|
||||
But this is an opportunity to exercise some of our newly won
|
||||
binding skills.
|
||||
If you're not interested, you can skip to the chapter's conclusion
|
||||
If you're not interested, you can skip to the guide's conclusion
|
||||
and not miss a thing.
|
||||
:marked
|
||||
Let's do something more strikingly visual.
|
||||
|
@ -670,7 +668,7 @@ figure.image-display
|
|||
:marked
|
||||
## Conclusion
|
||||
|
||||
The Angular form techniques discussed in this chapter take
|
||||
The Angular form techniques discussed in this guide take
|
||||
advantage of the following framework features to provide support for data modification, validation and more:
|
||||
|
||||
- An Angular HTML form template.
|
||||
|
|
Loading…
Reference in New Issue