
committed by
Jason Aden

parent
6ca780178c
commit
5a2531ee45
1
aio/content/examples/forms/src/app/app.component.html
Normal file
1
aio/content/examples/forms/src/app/app.component.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<app-hero-form></app-hero-form>
|
@ -3,6 +3,7 @@ import { Component } from '@angular/core';
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
template: '<app-hero-form></app-hero-form>'
|
templateUrl: './app.component.html',
|
||||||
|
styleUrls: ['./app.component.css']
|
||||||
})
|
})
|
||||||
export class AppComponent { }
|
export class AppComponent { }
|
||||||
|
@ -4,7 +4,7 @@ import { BrowserModule } from '@angular/platform-browser';
|
|||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { HeroFormComponent } from './hero-form.component';
|
import { HeroFormComponent } from './hero-form/hero-form.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -15,6 +15,7 @@ import { HeroFormComponent } from './hero-form.component';
|
|||||||
AppComponent,
|
AppComponent,
|
||||||
HeroFormComponent
|
HeroFormComponent
|
||||||
],
|
],
|
||||||
|
providers: [],
|
||||||
bootstrap: [ AppComponent ]
|
bootstrap: [ AppComponent ]
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
|
@ -2,11 +2,12 @@
|
|||||||
// #docregion , v1, final
|
// #docregion , v1, final
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
import { Hero } from './hero';
|
import { Hero } from '../hero';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-hero-form',
|
selector: 'app-hero-form',
|
||||||
templateUrl: './hero-form.component.html'
|
templateUrl: './hero-form.component.html',
|
||||||
|
styleUrls: ['./hero-form.component.css']
|
||||||
})
|
})
|
||||||
export class HeroFormComponent {
|
export class HeroFormComponent {
|
||||||
|
|
1
aio/content/examples/forms/src/styles.1.css
Normal file
1
aio/content/examples/forms/src/styles.1.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
@import url('https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css');
|
@ -29,19 +29,13 @@ You can run the <live-example></live-example> in Plunker and download the code f
|
|||||||
You can build forms by writing templates in the Angular [template syntax](guide/template-syntax) with
|
You can build forms by writing templates in the Angular [template syntax](guide/template-syntax) with
|
||||||
the form-specific directives and techniques described in this page.
|
the form-specific directives and techniques described in this page.
|
||||||
|
|
||||||
|
|
||||||
<div class="l-sub-section">
|
<div class="l-sub-section">
|
||||||
|
|
||||||
|
You can also use a reactive (or model-driven) approach to build forms.
|
||||||
|
However, this page focuses on template-driven forms.
|
||||||
You can also use a reactive (or model-driven) approach to build forms.
|
|
||||||
However, this page focuses on template-driven forms.
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
You can build almost any form with an Angular template—login forms, contact forms, and pretty much any business form.
|
You can build almost any form with an Angular template—login forms, contact forms, and pretty much any business form.
|
||||||
You can lay out the controls creatively, bind them to data, specify validation rules and display validation errors,
|
You can lay out the controls creatively, bind them to data, specify validation rules and display validation errors,
|
||||||
conditionally enable or disable specific controls, trigger built-in visual feedback, and much more.
|
conditionally enable or disable specific controls, trigger built-in visual feedback, and much more.
|
||||||
@ -51,13 +45,10 @@ otherwise wrestle with yourself.
|
|||||||
|
|
||||||
You'll learn to build a template-driven form that looks like this:
|
You'll learn to build a template-driven form that looks like this:
|
||||||
|
|
||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<img src="generated/images/guide/forms/hero-form-1.png" alt="Clean Form">
|
<img src="generated/images/guide/forms/hero-form-1.png" alt="Clean Form">
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The *Hero Employment Agency* uses this form to maintain personal information about heroes.
|
The *Hero Employment Agency* uses this form to maintain personal information about heroes.
|
||||||
Every hero needs a job. It's the company mission to match the right hero with the right crisis.
|
Every hero needs a job. It's the company mission to match the right hero with the right crisis.
|
||||||
|
|
||||||
@ -65,27 +56,18 @@ Two of the three fields on this form are required. Required fields have a green
|
|||||||
|
|
||||||
If you delete the hero name, the form displays a validation error in an attention-grabbing style:
|
If you delete the hero name, the form displays a validation error in an attention-grabbing style:
|
||||||
|
|
||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<img src="generated/images/guide/forms/hero-form-2.png" alt="Invalid, Name Required">
|
<img src="generated/images/guide/forms/hero-form-2.png" alt="Invalid, Name Required">
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Note that the *Submit* button is disabled, and the "required" bar to the left of the input control changes from green to red.
|
Note that the *Submit* button is disabled, and the "required" bar to the left of the input control changes from green to red.
|
||||||
|
|
||||||
|
|
||||||
<div class="l-sub-section">
|
<div class="l-sub-section">
|
||||||
|
|
||||||
|
You can customize the colors and location of the "required" bar with standard CSS.
|
||||||
|
|
||||||
You can customize the colors and location of the "required" bar with standard CSS.
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
You'll build this form in small steps:
|
You'll build this form in small steps:
|
||||||
|
|
||||||
1. Create the `Hero` model class.
|
1. Create the `Hero` model class.
|
||||||
@ -98,11 +80,15 @@ You'll build this form in small steps:
|
|||||||
1. Handle form submission with *ngSubmit*.
|
1. Handle form submission with *ngSubmit*.
|
||||||
1. Disable the form’s *Submit* button until the form is valid.
|
1. Disable the form’s *Submit* button until the form is valid.
|
||||||
|
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
Follow the [setup](guide/setup) instructions for creating a new project
|
Create a new project named <code>angular-forms</code>:
|
||||||
named angular-forms.
|
|
||||||
|
<code-example language="sh" class="code-shell">
|
||||||
|
|
||||||
|
ng new angular-forms
|
||||||
|
|
||||||
|
</code-example>
|
||||||
|
|
||||||
## Create the Hero model class
|
## Create the Hero model class
|
||||||
|
|
||||||
@ -113,15 +99,20 @@ A model can be as simple as a "property bag" that holds facts about a thing of a
|
|||||||
That describes well the `Hero` class with its three required fields (`id`, `name`, `power`)
|
That describes well the `Hero` class with its three required fields (`id`, `name`, `power`)
|
||||||
and one optional field (`alterEgo`).
|
and one optional field (`alterEgo`).
|
||||||
|
|
||||||
In the `app` directory, create the following file with the given content:
|
Using the Angular CLI, generate a new class named `Hero`:
|
||||||
|
|
||||||
|
<code-example language="sh" class="code-shell">
|
||||||
|
|
||||||
|
ng generate class Hero
|
||||||
|
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
With this content:
|
||||||
|
|
||||||
<code-example path="forms/src/app/hero.ts" title="src/app/hero.ts">
|
<code-example path="forms/src/app/hero.ts" title="src/app/hero.ts">
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
It's an anemic model with few requirements and no behavior. Perfect for the demo.
|
It's an anemic model with few requirements and no behavior. Perfect for the demo.
|
||||||
|
|
||||||
The TypeScript compiler generates a public field for each `public` constructor parameter and
|
The TypeScript compiler generates a public field for each `public` constructor parameter and
|
||||||
@ -131,28 +122,29 @@ The `alterEgo` is optional, so the constructor lets you omit it; note the questi
|
|||||||
|
|
||||||
You can create a new hero like this:
|
You can create a new hero like this:
|
||||||
|
|
||||||
|
<code-example path="forms/src/app/hero-form/hero-form.component.ts" linenums="false" region="SkyDog">
|
||||||
<code-example path="forms/src/app/hero-form.component.ts" linenums="false" title="src/app/hero-form.component.ts (SkyDog)" region="SkyDog">
|
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Create a form component
|
## Create a form component
|
||||||
|
|
||||||
An Angular form has two parts: an HTML-based _template_ and a component _class_
|
An Angular form has two parts: an HTML-based _template_ and a component _class_
|
||||||
to handle data and user interactions programmatically.
|
to handle data and user interactions programmatically.
|
||||||
Begin with the class because it states, in brief, what the hero editor can do.
|
Begin with the class because it states, in brief, what the hero editor can do.
|
||||||
|
|
||||||
Create the following file with the given content:
|
Using the Angular CLI, generate a new component named `HeroForm`:
|
||||||
|
|
||||||
|
<code-example language="sh" class="code-shell">
|
||||||
|
|
||||||
<code-example path="forms/src/app/hero-form.component.ts" linenums="false" title="src/app/hero-form.component.ts (v1)" region="v1">
|
ng generate component HeroForm
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
With this content:
|
||||||
|
|
||||||
|
<code-example path="forms/src/app/hero-form/hero-form.component.ts" linenums="false" title="src/app/hero-form/hero-form.component.ts (v1)" region="v1">
|
||||||
|
|
||||||
|
</code-example>
|
||||||
|
|
||||||
There’s nothing special about this component, nothing form-specific,
|
There’s nothing special about this component, nothing form-specific,
|
||||||
nothing to distinguish it from any component you've written before.
|
nothing to distinguish it from any component you've written before.
|
||||||
@ -173,21 +165,6 @@ parent component. This is not a concern now and these future changes won't affec
|
|||||||
* You added a `diagnostic` property to return a JSON representation of the model.
|
* You added a `diagnostic` property to return a JSON representation of the model.
|
||||||
It'll help you see what you're doing during development; you've left yourself a cleanup note to discard it later.
|
It'll help you see what you're doing during development; you've left yourself a cleanup note to discard it later.
|
||||||
|
|
||||||
### Why the separate template file?
|
|
||||||
|
|
||||||
Why don't you write the template inline in the component file as you often do elsewhere?
|
|
||||||
|
|
||||||
There is no "right" answer for all occasions. Inline templates are useful when they are short.
|
|
||||||
Most form templates aren't short. TypeScript and JavaScript files generally aren't the best place to
|
|
||||||
write (or read) large stretches of HTML, and few editors help with files that have a mix of HTML and code.
|
|
||||||
|
|
||||||
Form templates tend to be large, even when displaying a small number of fields,
|
|
||||||
so it's usually best to put the HTML template in a separate file.
|
|
||||||
You'll write that template file in a moment. First,
|
|
||||||
revise the `app.module.ts` and `app.component.ts` to make use of the new `HeroFormComponent`.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Revise *app.module.ts*
|
## Revise *app.module.ts*
|
||||||
|
|
||||||
`app.module.ts` defines the application's root module. In it you identify the external modules you'll use in the application
|
`app.module.ts` defines the application's root module. In it you identify the external modules you'll use in the application
|
||||||
@ -196,89 +173,57 @@ and declare the components that belong to this module, such as the `HeroFormComp
|
|||||||
Because template-driven forms are in their own module, you need to add the `FormsModule` to the array of
|
Because template-driven forms are in their own module, you need to add the `FormsModule` to the array of
|
||||||
`imports` for the application module before you can use forms.
|
`imports` for the application module before you can use forms.
|
||||||
|
|
||||||
Replace the contents of the "QuickStart" version with the following:
|
Update it with the following:
|
||||||
|
|
||||||
<code-example path="forms/src/app/app.module.ts" title="src/app/app.module.ts">
|
<code-example path="forms/src/app/app.module.ts" title="src/app/app.module.ts">
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="l-sub-section">
|
<div class="l-sub-section">
|
||||||
|
|
||||||
|
There are two changes:
|
||||||
|
|
||||||
|
1. You import `FormsModule`.
|
||||||
|
|
||||||
There are three changes:
|
1. You add the `FormsModule` to the list of `imports` defined in the `@NgModule` decorator. This gives the application
|
||||||
|
access to all of the template-driven forms features, including `ngModel`.
|
||||||
1. You import `FormsModule` and the new `HeroFormComponent`.
|
|
||||||
|
|
||||||
1. You add the `FormsModule` to the list of `imports` defined in the `@NgModule` decorator. This gives the application
|
|
||||||
access to all of the template-driven forms features, including `ngModel`.
|
|
||||||
|
|
||||||
1. You add the `HeroFormComponent` to the list of `declarations` defined in the `@NgModule` decorator. This makes
|
|
||||||
the `HeroFormComponent` component visible throughout this module.
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="alert is-important">
|
<div class="alert is-important">
|
||||||
|
|
||||||
|
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.
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
## Revise *app.component.html*
|
||||||
|
|
||||||
|
|
||||||
## Revise *app.component.ts*
|
|
||||||
|
|
||||||
`AppComponent` is the application's root component. It will host the new `HeroFormComponent`.
|
`AppComponent` is the application's root component. It will host the new `HeroFormComponent`.
|
||||||
|
|
||||||
Replace the contents of the "QuickStart" version with the following:
|
Replace the contents of its template with the following:
|
||||||
|
|
||||||
|
<code-example path="forms/src/app/app.component.html" title="src/app/app.component.html">
|
||||||
<code-example path="forms/src/app/app.component.ts" title="src/app/app.component.ts">
|
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="l-sub-section">
|
<div class="l-sub-section">
|
||||||
|
|
||||||
|
There are only two changes.
|
||||||
|
The `template` is simply the new element tag identified by the component's `selector` property.
|
||||||
There are only two changes.
|
This displays the hero form when the application component is loaded.
|
||||||
The `template` is simply the new element tag identified by the component's `selector` property.
|
Don't forget to remove the `name` field from the class body as well.
|
||||||
This displays the hero form when the application component is loaded.
|
|
||||||
You've also dropped the `name` field from the class body.
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Create an initial HTML form template
|
## Create an initial HTML form template
|
||||||
|
|
||||||
Create the template file with the following contents:
|
Update the template file with the following contents:
|
||||||
|
|
||||||
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="start" title="src/app/hero-form/hero-form.component.html">
|
||||||
<code-example path="forms/src/app/hero-form.component.html" region="start" title="src/app/hero-form.component.html">
|
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The language is simply HTML5. You're presenting two of the `Hero` fields, `name` and `alterEgo`, and
|
The language is simply HTML5. You're presenting two of the `Hero` fields, `name` and `alterEgo`, and
|
||||||
opening them up for user input in input boxes.
|
opening them up for user input in input boxes.
|
||||||
|
|
||||||
@ -289,52 +234,34 @@ You added a *Submit* button at the bottom with some classes on it for styling.
|
|||||||
|
|
||||||
*You're not using Angular yet*. There are no bindings or extra directives, just layout.
|
*You're not using Angular yet*. There are no bindings or extra directives, just layout.
|
||||||
|
|
||||||
|
|
||||||
<div class="l-sub-section">
|
<div class="l-sub-section">
|
||||||
|
|
||||||
|
In template driven forms, if you've imported `FormsModule`, you don't have to do anything
|
||||||
|
to the `<form>` tag in order to make use of `FormsModule`. Continue on to see how this works.
|
||||||
In template driven forms, if you've imported `FormsModule`, you don't have to do anything
|
|
||||||
to the `<form>` tag in order to make use of `FormsModule`. Continue on to see how this works.
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The `container`, `form-group`, `form-control`, and `btn` classes
|
The `container`, `form-group`, `form-control`, and `btn` classes
|
||||||
come from [Twitter Bootstrap](http://getbootstrap.com/css/). These classes are purely cosmetic.
|
come from [Twitter Bootstrap](http://getbootstrap.com/css/). These classes are purely cosmetic.
|
||||||
Bootstrap gives the form a little style.
|
Bootstrap gives the form a little style.
|
||||||
|
|
||||||
|
|
||||||
<div class="callout is-important">
|
<div class="callout is-important">
|
||||||
|
|
||||||
|
<header>
|
||||||
|
|
||||||
<header>
|
|
||||||
Angular forms don't require a style library
|
Angular forms don't require a style library
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Angular makes no use of the `container`, `form-group`, `form-control`, and `btn` classes or
|
|
||||||
the styles of any external library. Angular apps can use any CSS library or none at all.
|
|
||||||
|
|
||||||
|
Angular makes no use of the `container`, `form-group`, `form-control`, and `btn` classes or
|
||||||
|
the styles of any external library. Angular apps can use any CSS library or none at all.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
To add the stylesheet, open `styles.css` and add the following import line at the top:
|
||||||
|
|
||||||
|
<code-example path="forms/src/styles.1.css" linenums="false" title="src/styles.css">
|
||||||
To add the stylesheet, open `index.html` and add the following link to the `<head>`:
|
|
||||||
|
|
||||||
|
|
||||||
<code-example path="forms/src/index.html" linenums="false" title="src/index.html (bootstrap)" region="bootstrap">
|
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Add powers with _*ngFor_
|
## Add powers with _*ngFor_
|
||||||
|
|
||||||
The hero must choose one superpower from a fixed list of agency-approved powers.
|
The hero must choose one superpower from a fixed list of agency-approved powers.
|
||||||
@ -346,13 +273,10 @@ a technique seen previously in the [Displaying Data](guide/displaying-data) page
|
|||||||
|
|
||||||
Add the following HTML *immediately below* the *Alter Ego* group:
|
Add the following HTML *immediately below* the *Alter Ego* group:
|
||||||
|
|
||||||
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (powers)" region="powers">
|
||||||
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (powers)" region="powers">
|
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
This code repeats the `<option>` tag for each power in the list of powers.
|
This code repeats the `<option>` tag for each power in the list of powers.
|
||||||
The `pow` template input variable is a different power in each iteration;
|
The `pow` template input variable is a different power in each iteration;
|
||||||
you display its name using the interpolation syntax.
|
you display its name using the interpolation syntax.
|
||||||
@ -363,13 +287,11 @@ you display its name using the interpolation syntax.
|
|||||||
|
|
||||||
Running the app right now would be disappointing.
|
Running the app right now would be disappointing.
|
||||||
|
|
||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<img src="generated/images/guide/forms/hero-form-3.png" alt="Early form with no binding">
|
<img src="generated/images/guide/forms/hero-form-3.png" alt="Early form with no binding">
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
You don't see hero data because you're not binding to the `Hero` yet.
|
You don't see hero data because you're not binding to the `Hero` yet.
|
||||||
You know how to do that from earlier pages.
|
You know how to do that from earlier pages.
|
||||||
[Displaying Data](guide/displaying-data) teaches property binding.
|
[Displaying Data](guide/displaying-data) teaches property binding.
|
||||||
@ -384,113 +306,83 @@ makes binding the form to the model easy.
|
|||||||
|
|
||||||
Find the `<input>` tag for *Name* and update it like this:
|
Find the `<input>` tag for *Name* and update it like this:
|
||||||
|
|
||||||
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModelName-1">
|
||||||
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="ngModelName-1">
|
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="l-sub-section">
|
<div class="l-sub-section">
|
||||||
|
|
||||||
|
You added a diagnostic interpolation after the input tag
|
||||||
|
so you can see what you're doing.
|
||||||
You added a diagnostic interpolation after the input tag
|
You left yourself a note to throw it away when you're done.
|
||||||
so you can see what you're doing.
|
|
||||||
You left yourself a note to throw it away when you're done.
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Focus on the binding syntax: `[(ngModel)]="..."`.
|
Focus on the binding syntax: `[(ngModel)]="..."`.
|
||||||
|
|
||||||
You need one more addition to display the data. Declare
|
You need one more addition to display the data. Declare
|
||||||
a template variable for the form. Update the `<form>` tag with
|
a template variable for the form. Update the `<form>` tag with
|
||||||
`#heroForm="ngForm"` as follows:
|
`#heroForm="ngForm"` as follows:
|
||||||
|
|
||||||
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (excerpt)" region="template-variable">
|
||||||
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="template-variable">
|
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The variable `heroForm` is now a reference to the `NgForm` directive that governs the form as a whole.
|
The variable `heroForm` is now a reference to the `NgForm` directive that governs the form as a whole.
|
||||||
|
|
||||||
|
|
||||||
<div class="l-sub-section">
|
<div class="l-sub-section">
|
||||||
|
|
||||||
{@a ngForm}
|
{@a ngForm}
|
||||||
|
|
||||||
### The _NgForm_ directive
|
### The _NgForm_ directive
|
||||||
|
|
||||||
What `NgForm` directive?
|
What `NgForm` directive?
|
||||||
You didn't add an [NgForm](api/forms/NgForm) directive.
|
You didn't add an [NgForm](api/forms/NgForm) directive.
|
||||||
|
|
||||||
Angular did. Angular automatically creates and attaches an `NgForm` directive to the `<form>` tag.
|
Angular did. Angular automatically creates and attaches an `NgForm` directive to the `<form>` tag.
|
||||||
|
|
||||||
The `NgForm` directive supplements the `form` element with additional features.
|
|
||||||
It holds the controls you created for the elements with an `ngModel` directive
|
|
||||||
and `name` attribute, and monitors their properties, including their validity.
|
|
||||||
It also has its own `valid` property which is true only *if every contained
|
|
||||||
control* is valid.
|
|
||||||
|
|
||||||
|
The `NgForm` directive supplements the `form` element with additional features.
|
||||||
|
It holds the controls you created for the elements with an `ngModel` directive
|
||||||
|
and `name` attribute, and monitors their properties, including their validity.
|
||||||
|
It also has its own `valid` property which is true only *if every contained
|
||||||
|
control* is valid.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
If you ran the app now and started typing in the *Name* input box,
|
If you ran the app now and started typing in the *Name* input box,
|
||||||
adding and deleting characters, you'd see them appear and disappear
|
adding and deleting characters, you'd see them appear and disappear
|
||||||
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>
|
<figure>
|
||||||
<img src="generated/images/guide/forms/ng-model-in-action.png" alt="ngModel in action">
|
<img src="generated/images/guide/forms/ng-model-in-action.png" alt="ngModel in action">
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The diagnostic is evidence that values really are flowing from the input box to the model and
|
The diagnostic is evidence that values really are flowing from the input box to the model and
|
||||||
back again.
|
back again.
|
||||||
|
|
||||||
|
|
||||||
<div class="l-sub-section">
|
<div class="l-sub-section">
|
||||||
|
|
||||||
|
That's *two-way data binding*.
|
||||||
|
For more information, see
|
||||||
That's *two-way data binding*.
|
[Two-way binding with NgModel](guide/template-syntax#ngModel) on the
|
||||||
For more information, see
|
the [Template Syntax](guide/template-syntax) page.
|
||||||
[Two-way binding with NgModel](guide/template-syntax#ngModel) on the
|
|
||||||
the [Template Syntax](guide/template-syntax) page.
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Notice that you also added a `name` attribute to the `<input>` tag and set it to "name",
|
Notice that you also added a `name` attribute to the `<input>` tag and set it to "name",
|
||||||
which makes sense for the hero's name. Any unique value will do, but using a descriptive name is helpful.
|
which makes sense for the hero's name. Any unique value will do, but using a descriptive name is helpful.
|
||||||
Defining a `name` attribute is a requirement when using `[(ngModel)]` in combination with a form.
|
Defining a `name` attribute is a requirement when using `[(ngModel)]` in combination with a form.
|
||||||
|
|
||||||
|
|
||||||
<div class="l-sub-section">
|
<div class="l-sub-section">
|
||||||
|
|
||||||
|
Internally, Angular creates `FormControl` instances and
|
||||||
|
registers them with an `NgForm` directive that Angular attached to the `<form>` tag.
|
||||||
Internally, Angular creates `FormControl` instances and
|
Each `FormControl` is registered under the name you assigned to the `name` attribute.
|
||||||
registers them with an `NgForm` directive that Angular attached to the `<form>` tag.
|
Read more in the previous section, [The NgForm directive](guide/forms#ngForm).
|
||||||
Each `FormControl` is registered under the name you assigned to the `name` attribute.
|
|
||||||
Read more in the previous section, [The NgForm directive](guide/forms#ngForm).
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Add similar `[(ngModel)]` bindings and `name` attributes to *Alter Ego* and *Hero Power*.
|
Add similar `[(ngModel)]` bindings and `name` attributes to *Alter Ego* and *Hero Power*.
|
||||||
You'll ditch the input box binding message
|
You'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.
|
||||||
@ -498,42 +390,29 @@ Then you can confirm that two-way data binding works *for the entire hero model*
|
|||||||
|
|
||||||
After revision, the core of the form should look like this:
|
After revision, the core of the form should look like this:
|
||||||
|
|
||||||
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModel-2">
|
||||||
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="ngModel-2">
|
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="l-sub-section">
|
<div class="l-sub-section">
|
||||||
|
|
||||||
|
* Each input element has an `id` property that is used by the `label` element's `for` attribute
|
||||||
|
to match the label to its input control.
|
||||||
* Each input element has an `id` property that is used by the `label` element's `for` attribute
|
* Each input element has a `name` property that is required by Angular forms to register the control with the form.
|
||||||
to match the label to its input control.
|
|
||||||
* Each input element has a `name` property that is required by Angular forms to register the control with the form.
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
If you run the app now and change every hero model property, the form might display like this:
|
If you run the app now and change every hero model property, the form might display like this:
|
||||||
|
|
||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<img src="generated/images/guide/forms/ng-model-in-action-2.png" alt="ngModel in action">
|
<img src="generated/images/guide/forms/ng-model-in-action-2.png" alt="ngModel in action">
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The diagnostic near the top of the form
|
The diagnostic near the top of the form
|
||||||
confirms that all of your changes are reflected in the model.
|
confirms that all of your changes are reflected in the model.
|
||||||
|
|
||||||
*Delete* the `{{diagnostic}}` binding at the top as it has served its purpose.
|
*Delete* the `{{diagnostic}}` binding at the top as it has served its purpose.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Track control state and validity with _ngModel_
|
## Track control state and validity with _ngModel_
|
||||||
|
|
||||||
Using `ngModel` in a form gives you more than just two-way data binding. It also tells
|
Using `ngModel` in a form gives you more than just two-way data binding. It also tells
|
||||||
@ -542,7 +421,6 @@ you if the user touched the control, if the value changed, or if the value becam
|
|||||||
The *NgModel* directive doesn't just track state; it updates the control with special Angular CSS classes that reflect the state.
|
The *NgModel* directive doesn't just track state; it updates the control with special Angular CSS classes that reflect the state.
|
||||||
You can leverage those class names to change the appearance of the control.
|
You can leverage those class names to change the appearance of the control.
|
||||||
|
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
@ -611,18 +489,13 @@ You can leverage those class names to change the appearance of the control.
|
|||||||
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Temporarily add a [template reference variable](guide/template-syntax#ref-vars) named `spy`
|
Temporarily add a [template reference variable](guide/template-syntax#ref-vars) named `spy`
|
||||||
to the _Name_ `<input>` tag and use it to display the input's CSS classes.
|
to the _Name_ `<input>` tag and use it to display the input's CSS classes.
|
||||||
|
|
||||||
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModelName-2">
|
||||||
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="ngModelName-2">
|
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Now run the app and look at the _Name_ input box.
|
Now run the app and look at the _Name_ input box.
|
||||||
Follow these steps *precisely*:
|
Follow these steps *precisely*:
|
||||||
|
|
||||||
@ -633,61 +506,44 @@ Follow these steps *precisely*:
|
|||||||
|
|
||||||
The actions and effects are as follows:
|
The actions and effects are as follows:
|
||||||
|
|
||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<img src="generated/images/guide/forms/control-state-transitions-anim.gif" alt="Control State Transition">
|
<img src="generated/images/guide/forms/control-state-transitions-anim.gif" alt="Control State Transition">
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
You should see the following transitions and class names:
|
You should see the following transitions and class names:
|
||||||
|
|
||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<img src="generated/images/guide/forms/ng-control-class-changes.png" alt="Control state transitions">
|
<img src="generated/images/guide/forms/ng-control-class-changes.png" alt="Control state transitions">
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The `ng-valid`/`ng-invalid` pair is the most interesting, because you want to send a
|
The `ng-valid`/`ng-invalid` pair is the most interesting, because you want to send a
|
||||||
strong visual signal when the values are invalid. You also want to mark required fields.
|
strong visual signal when the values are invalid. You also want to mark required fields.
|
||||||
To create such visual feedback, add definitions for the `ng-*` CSS classes.
|
To create such visual feedback, add definitions for the `ng-*` CSS classes.
|
||||||
|
|
||||||
*Delete* the `#spy` template reference variable and the `TODO` as they have served their purpose.
|
*Delete* the `#spy` template reference variable and the `TODO` as they have served their purpose.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Add custom CSS for visual feedback
|
## Add custom CSS for visual feedback
|
||||||
|
|
||||||
You can mark required fields and invalid data at the same time with a colored bar
|
You can mark required fields and invalid data at the same time with a colored bar
|
||||||
on the left of the input box:
|
on the left of the input box:
|
||||||
|
|
||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<img src="generated/images/guide/forms/validity-required-indicator.png" alt="Invalid Form">
|
<img src="generated/images/guide/forms/validity-required-indicator.png" alt="Invalid Form">
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
You achieve this effect by adding these class definitions to a new `forms.css` file
|
You achieve this effect by adding these class definitions to a new `forms.css` file
|
||||||
that you add to the project as a sibling to `index.html`:
|
that you add to the project as a sibling to `index.html`:
|
||||||
|
|
||||||
|
|
||||||
<code-example path="forms/src/assets/forms.css" title="src/assets/forms.css">
|
<code-example path="forms/src/assets/forms.css" title="src/assets/forms.css">
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Update the `<head>` of `index.html` to include this style sheet:
|
Update the `<head>` of `index.html` to include this style sheet:
|
||||||
|
|
||||||
|
|
||||||
<code-example path="forms/src/index.html" linenums="false" title="src/index.html (styles)" region="styles">
|
<code-example path="forms/src/index.html" linenums="false" title="src/index.html (styles)" region="styles">
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Show and hide validation error messages
|
## Show and hide validation error messages
|
||||||
|
|
||||||
You can improve the form. The _Name_ input box is required and clearing it turns the bar red.
|
You can improve the form. The _Name_ input box is required and clearing it turns the bar red.
|
||||||
@ -696,13 +552,10 @@ Leverage the control's state to reveal a helpful message.
|
|||||||
|
|
||||||
When the user deletes the name, the form should look like this:
|
When the user deletes the name, the form should look like this:
|
||||||
|
|
||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<img src="generated/images/guide/forms/name-required-error.png" alt="Name required">
|
<img src="generated/images/guide/forms/name-required-error.png" alt="Name required">
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
To achieve this effect, extend the `<input>` tag with the following:
|
To achieve this effect, extend the `<input>` tag with the following:
|
||||||
|
|
||||||
* A [template reference variable](guide/template-syntax#ref-vars).
|
* A [template reference variable](guide/template-syntax#ref-vars).
|
||||||
@ -710,41 +563,29 @@ To achieve this effect, extend the `<input>` tag with the following:
|
|||||||
|
|
||||||
Here's an example of an error message added to the _name_ input box:
|
Here's an example of an error message added to the _name_ input box:
|
||||||
|
|
||||||
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (excerpt)" region="name-with-error-msg">
|
||||||
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="name-with-error-msg">
|
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
You need a template reference variable to access the input box's Angular control from within the template.
|
You need a template reference variable to access the input box's Angular control from within the template.
|
||||||
Here you created a variable called `name` and gave it the value "ngModel".
|
Here you created a variable called `name` and gave it the value "ngModel".
|
||||||
|
|
||||||
|
|
||||||
<div class="l-sub-section">
|
<div class="l-sub-section">
|
||||||
|
|
||||||
|
Why "ngModel"?
|
||||||
|
A directive's [exportAs](api/core/Directive) property
|
||||||
Why "ngModel"?
|
tells Angular how to link the reference variable to the directive.
|
||||||
A directive's [exportAs](api/core/Directive) property
|
You set `name` to `ngModel` because the `ngModel` directive's `exportAs` property happens to be "ngModel".
|
||||||
tells Angular how to link the reference variable to the directive.
|
|
||||||
You set `name` to `ngModel` because the `ngModel` directive's `exportAs` property happens to be "ngModel".
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
You control visibility of the name error message by binding properties of the `name`
|
You control visibility of the name error message by binding properties of the `name`
|
||||||
control to the message `<div>` element's `hidden` property.
|
control to the message `<div>` element's `hidden` property.
|
||||||
|
|
||||||
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (hidden-error-msg)" region="hidden-error-msg">
|
||||||
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (hidden-error-msg)" region="hidden-error-msg">
|
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
In this example, you hide the message when the control is valid or pristine;
|
In this example, you 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.
|
"pristine" means the user hasn't changed the value since it was displayed in this form.
|
||||||
|
|
||||||
@ -767,19 +608,14 @@ power to valid values.
|
|||||||
Now you'll add a new hero in this form.
|
Now you'll add a new hero in this form.
|
||||||
Place a *New Hero* button at the bottom of the form and bind its click event to a `newHero` component method.
|
Place a *New Hero* button at the bottom of the form and bind its click event to a `newHero` component method.
|
||||||
|
|
||||||
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="new-hero-button-no-reset" title="src/app/hero-form/hero-form.component.html (New Hero button)">
|
||||||
<code-example path="forms/src/app/hero-form.component.html" region="new-hero-button-no-reset" title="src/app/hero-form.component.html (New Hero button)">
|
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
<code-example path="forms/src/app/hero-form/hero-form.component.ts" region="new-hero" title="src/app/hero-form/hero-form.component.ts (New Hero method)" linenums="false">
|
||||||
|
|
||||||
<code-example path="forms/src/app/hero-form.component.ts" region="new-hero" title="src/app/hero-form.component.ts (New Hero method)" linenums="false">
|
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Run the application again, click the *New Hero* button, and the form clears.
|
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.
|
The *required* bars to the left of the input box are red, indicating invalid `name` and `power` properties.
|
||||||
That's understandable as these are required fields.
|
That's understandable as these are required fields.
|
||||||
@ -797,17 +633,12 @@ Replacing the hero object *did not restore the pristine state* of the form contr
|
|||||||
You have to clear all of the flags imperatively, which you can do
|
You have to clear all of the flags imperatively, which you can do
|
||||||
by calling the form's `reset()` method after calling the `newHero()` method.
|
by calling the form's `reset()` method after calling the `newHero()` method.
|
||||||
|
|
||||||
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="new-hero-button-form-reset" title="src/app/hero-form/hero-form.component.html (Reset the form)">
|
||||||
<code-example path="forms/src/app/hero-form.component.html" region="new-hero-button-form-reset" title="src/app/hero-form.component.html (Reset the form)">
|
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Now clicking "New Hero" resets both the form and its control flags.
|
Now clicking "New Hero" resets both the form and its control flags.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Submit the form with _ngSubmit_
|
## 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.
|
||||||
@ -819,13 +650,10 @@ A "form submit" is useless at the moment.
|
|||||||
To make it useful, bind the form's `ngSubmit` event property
|
To make it useful, bind the form's `ngSubmit` event property
|
||||||
to the hero form component's `onSubmit()` method:
|
to the hero form component's `onSubmit()` method:
|
||||||
|
|
||||||
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (ngSubmit)" region="ngSubmit">
|
||||||
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (ngSubmit)" region="ngSubmit">
|
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
You'd already defined a template reference variable,
|
You'd already defined a template reference variable,
|
||||||
`#heroForm`, and initialized it with the value "ngForm".
|
`#heroForm`, and initialized it with the value "ngForm".
|
||||||
Now, use that variable to access the form with the Submit button.
|
Now, use that variable to access the form with the Submit button.
|
||||||
@ -835,13 +663,10 @@ You'll bind the form's overall validity via
|
|||||||
the `heroForm` variable to the button's `disabled` property
|
the `heroForm` variable to the button's `disabled` property
|
||||||
using an event binding. Here's the code:
|
using an event binding. Here's the code:
|
||||||
|
|
||||||
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (submit-button)" region="submit-button">
|
||||||
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (submit-button)" region="submit-button">
|
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
If you run the application now, you find that the button is enabled—although
|
If you run the application now, you find that the button is enabled—although
|
||||||
it doesn't do anything useful yet.
|
it doesn't do anything useful yet.
|
||||||
|
|
||||||
@ -857,65 +682,48 @@ For you, it was as simple as this:
|
|||||||
1. Define a template reference variable on the (enhanced) form element.
|
1. Define a template reference variable on the (enhanced) form element.
|
||||||
2. Refer to that variable in a button many lines away.
|
2. Refer to that variable in a button many lines away.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Toggle two form regions (extra credit)
|
## Toggle two form regions (extra credit)
|
||||||
|
|
||||||
Submitting the form isn't terribly dramatic at the moment.
|
Submitting the form isn't terribly dramatic at the moment.
|
||||||
|
|
||||||
|
|
||||||
<div class="l-sub-section">
|
<div class="l-sub-section">
|
||||||
|
|
||||||
|
An unsurprising observation for a demo. To be honest,
|
||||||
|
jazzing it up won't teach you anything new about forms.
|
||||||
An unsurprising observation for a demo. To be honest,
|
But this is an opportunity to exercise some of your newly won
|
||||||
jazzing it up won't teach you anything new about forms.
|
binding skills.
|
||||||
But this is an opportunity to exercise some of your newly won
|
If you aren't interested, skip to this page's conclusion.
|
||||||
binding skills.
|
|
||||||
If you aren't interested, skip to this page's conclusion.
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
For a more strikingly visual effect,
|
For a more strikingly visual effect,
|
||||||
hide the data entry area and display something else.
|
hide the data entry area and display something else.
|
||||||
|
|
||||||
Wrap the form in a `<div>` and bind
|
Wrap the form in a `<div>` and bind
|
||||||
its `hidden` property to the `HeroFormComponent.submitted` property.
|
its `hidden` property to the `HeroFormComponent.submitted` property.
|
||||||
|
|
||||||
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (excerpt)" region="edit-div">
|
||||||
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="edit-div">
|
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The main form is visible from the start because the
|
The main form is visible from the start because the
|
||||||
`submitted` property is false until you submit the form,
|
`submitted` property is false until you submit the form,
|
||||||
as this fragment from the `HeroFormComponent` shows:
|
as this fragment from the `HeroFormComponent` shows:
|
||||||
|
|
||||||
|
<code-example path="forms/src/app/hero-form/hero-form.component.ts" linenums="false" title="src/app/hero-form/hero-form.component.ts (submitted)" region="submitted">
|
||||||
<code-example path="forms/src/app/hero-form.component.ts" linenums="false" title="src/app/hero-form.component.ts (submitted)" region="submitted">
|
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
When you click the *Submit* button, the `submitted` flag becomes true and the form disappears
|
When you click the *Submit* button, the `submitted` flag becomes true and the form disappears
|
||||||
as planned.
|
as planned.
|
||||||
|
|
||||||
Now the app needs to show something else while the form is in the submitted state.
|
Now the app needs to show something else while the form is in the submitted state.
|
||||||
Add the following HTML below the `<div>` wrapper you just wrote:
|
Add the following HTML below the `<div>` wrapper you just wrote:
|
||||||
|
|
||||||
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (excerpt)" region="submitted">
|
||||||
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="submitted">
|
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
There's the hero again, displayed read-only with interpolation bindings.
|
There's the hero again, displayed read-only with interpolation bindings.
|
||||||
This `<div>` appears only while the component is in the submitted state.
|
This `<div>` appears only while the component is in the submitted state.
|
||||||
|
|
||||||
@ -924,9 +732,7 @@ that clears the `submitted` flag.
|
|||||||
|
|
||||||
When you click the *Edit* button, this block disappears and the editable form reappears.
|
When you click the *Edit* button, this block disappears and the editable form reappears.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
The Angular form discussed in this page takes advantage of the following
|
The Angular form discussed in this page takes advantage of the following
|
||||||
framework features to provide support for data modification, validation, and more:
|
framework features to provide support for data modification, validation, and more:
|
||||||
@ -941,89 +747,15 @@ framework features to provide support for data modification, validation, and mor
|
|||||||
* 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.
|
||||||
|
|
||||||
The final project folder structure should look like this:
|
|
||||||
|
|
||||||
|
|
||||||
<div class='filetree'>
|
|
||||||
|
|
||||||
<div class='file'>
|
|
||||||
angular-forms
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='children'>
|
|
||||||
|
|
||||||
<div class='file'>
|
|
||||||
src
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='children'>
|
|
||||||
|
|
||||||
<div class='file'>
|
|
||||||
app
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='children'>
|
|
||||||
|
|
||||||
<div class='file'>
|
|
||||||
app.component.ts
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='file'>
|
|
||||||
app.module.ts
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='file'>
|
|
||||||
hero.ts
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='file'>
|
|
||||||
hero-form.component.html
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='file'>
|
|
||||||
hero-form.component.ts
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='file'>
|
|
||||||
main.ts
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='file'>
|
|
||||||
tsconfig.json
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='file'>
|
|
||||||
index.html
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='file'>
|
|
||||||
node_modules ...
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='file'>
|
|
||||||
package.json
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Here’s the code for the final version of the application:
|
Here’s the code for the final version of the application:
|
||||||
|
|
||||||
|
|
||||||
<code-tabs>
|
<code-tabs>
|
||||||
|
|
||||||
<code-pane title="hero-form.component.ts" path="forms/src/app/hero-form.component.ts" region="final">
|
<code-pane title="hero-form/hero-form.component.ts" path="forms/src/app/hero-form/hero-form.component.ts" region="final">
|
||||||
|
|
||||||
</code-pane>
|
</code-pane>
|
||||||
|
|
||||||
<code-pane title="hero-form.component.html" path="forms/src/app/hero-form.component.html" region="final">
|
<code-pane title="hero-form/hero-form.component.html" path="forms/src/app/hero-form/hero-form.component.html" region="final">
|
||||||
|
|
||||||
</code-pane>
|
</code-pane>
|
||||||
|
|
||||||
@ -1035,6 +767,10 @@ Here’s the code for the final version of the application:
|
|||||||
|
|
||||||
</code-pane>
|
</code-pane>
|
||||||
|
|
||||||
|
<code-pane title="app.component.html" path="forms/src/app/app.component.html">
|
||||||
|
|
||||||
|
</code-pane>
|
||||||
|
|
||||||
<code-pane title="app.component.ts" path="forms/src/app/app.component.ts">
|
<code-pane title="app.component.ts" path="forms/src/app/app.component.ts">
|
||||||
|
|
||||||
</code-pane>
|
</code-pane>
|
||||||
@ -1043,10 +779,6 @@ Here’s the code for the final version of the application:
|
|||||||
|
|
||||||
</code-pane>
|
</code-pane>
|
||||||
|
|
||||||
<code-pane title="index.html" path="forms/src/index.html">
|
|
||||||
|
|
||||||
</code-pane>
|
|
||||||
|
|
||||||
<code-pane title="forms.css" path="forms/src/assets/forms.css">
|
<code-pane title="forms.css" path="forms/src/assets/forms.css">
|
||||||
|
|
||||||
</code-pane>
|
</code-pane>
|
||||||
|
Reference in New Issue
Block a user