From 382aa50c54e581fa74e0b6324c7103e4b894a1fc Mon Sep 17 00:00:00 2001 From: Judy Bogart Date: Wed, 8 Apr 2020 14:08:04 -0700 Subject: [PATCH] docs: refactor template-driven forms doc as a tutorial (#36732) rework content to meet current documentation standards and conventions, structure as tutorial document type PR Close #36732 --- .../examples/forms/src/app/app.module.ts | 2 +- .../app/hero-form/hero-form.component.html | 9 - .../src/app/hero-form/hero-form.component.ts | 2 +- aio/content/guide/forms-overview.md | 11 +- aio/content/guide/forms.md | 703 +++++++----------- .../forms/control-state-transitions-anim.gif | Bin 226076 -> 0 bytes .../guide/forms/ng-control-class-changes.png | Bin 41737 -> 0 bytes aio/content/navigation.json | 10 +- 8 files changed, 269 insertions(+), 468 deletions(-) delete mode 100644 aio/content/images/guide/forms/control-state-transitions-anim.gif delete mode 100644 aio/content/images/guide/forms/ng-control-class-changes.png diff --git a/aio/content/examples/forms/src/app/app.module.ts b/aio/content/examples/forms/src/app/app.module.ts index 51b9b9afe2..94d11f726e 100644 --- a/aio/content/examples/forms/src/app/app.module.ts +++ b/aio/content/examples/forms/src/app/app.module.ts @@ -3,7 +3,7 @@ import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; -import { AppComponent } from './app.component'; +import { AppComponent } from './app.component'; import { HeroFormComponent } from './hero-form/hero-form.component'; @NgModule({ diff --git a/aio/content/examples/forms/src/app/hero-form/hero-form.component.html b/aio/content/examples/forms/src/app/hero-form/hero-form.component.html index 0a270bc7f0..dbbc1c3908 100644 --- a/aio/content/examples/forms/src/app/hero-form/hero-form.component.html +++ b/aio/content/examples/forms/src/app/hero-form/hero-form.component.html @@ -200,13 +200,4 @@ (ngModelChange)="model.name = $event"> TODO: remove this: {{model.name}} -
- - -
TODO: remove this: {{spy.className}} - - diff --git a/aio/content/examples/forms/src/app/hero-form/hero-form.component.ts b/aio/content/examples/forms/src/app/hero-form/hero-form.component.ts index bc5fe12ef8..19f83e7d96 100644 --- a/aio/content/examples/forms/src/app/hero-form/hero-form.component.ts +++ b/aio/content/examples/forms/src/app/hero-form/hero-form.component.ts @@ -2,7 +2,7 @@ // #docregion , v1, final import { Component } from '@angular/core'; -import { Hero } from '../hero'; +import { Hero } from '../hero'; @Component({ selector: 'app-hero-form', diff --git a/aio/content/guide/forms-overview.md b/aio/content/guide/forms-overview.md index aca51c47f3..1828c70348 100644 --- a/aio/content/guide/forms-overview.md +++ b/aio/content/guide/forms-overview.md @@ -279,11 +279,12 @@ Here are the steps performed in the model to view test. To learn more about reactive forms, see the following guides: -* [Reactive forms](guide/reactive-forms) guide -* [Form validation](guide/form-validation#reactive-form-validation) guide -* [Building dynamic forms](guide/dynamic-form) tutorial +* [Reactive forms](guide/reactive-forms) +* [Form validation](guide/form-validation#reactive-form-validation) +* [Dynamic forms](guide/dynamic-form) To learn more about template-driven forms, see the following guides: -* [Building a template-driven form](guide/forms#template-driven-forms) tutorial -* [Form validation](guide/form-validation#template-driven-validation) guide +* [Building a template-driven form](guide/forms) tutorial +* [Form validation](guide/form-validation#template-driven-validation) +* `NgForm` directive API reference diff --git a/aio/content/guide/forms.md b/aio/content/guide/forms.md index e7bd8ea30b..ad00df810c 100644 --- a/aio/content/guide/forms.md +++ b/aio/content/guide/forms.md @@ -1,389 +1,234 @@ -# Template-driven forms - -Forms are the mainstay of business applications. -You use forms to log in, submit a help request, place an order, book a flight, -schedule a meeting, and perform countless other data-entry tasks. - -In developing a form, it's important to create a data-entry experience that guides the -user efficiently and effectively through the workflow. - -
- - For the sample app that this page describes, see the . - -
- -## Introduction to Template-driven forms - -Developing forms requires design skills (which are out of scope for this page), as well as framework support for -*two-way data binding, change tracking, validation, and error handling*, -which you'll learn about on this page. - -This page shows you how to build a simple form from scratch. Along the way you'll learn how to: - -* Build an Angular form with a component and template. -* Use `ngModel` to create two-way data bindings for reading and writing input-control values. -* Track state changes and the validity of form controls. -* Provide visual feedback using special CSS classes that track the state of the controls. -* Display validation errors to users and enable/disable form controls. -* Share information across HTML elements using template reference variables. +# Building a template-driven form {@a template-driven} -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. +This tutorial shows you how to create a template-driven form whose control elements are bound to data properties, with input validation to maintain data integrity and styling to improve the user experience. + +Template-driven forms use [two-way data binding](guide/architecture-components#data-binding "Intro to 2-way data binding") to update the data model in the component as changes are made in the template and vice versa.
- You can also use a reactive (or model-driven) approach to build forms. - However, this page focuses on template-driven forms. +Angular supports two design approaches for interactive forms. You can build forms by writing templates using Angular [template syntax and directives](guide/glossary#template "Definition of template terms") with the form-specific directives and techniques described in this tutorial, or you can use a reactive (or model-driven) approach to build forms. + +Template-driven forms are suitable for small or simple forms, while reactive forms are more scalable and suitable for complex forms. +For a comparison of the two approaches, see [Introduction to Forms](guide/forms-overview "Overview of Angular forms.")
-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 build almost any kind of form with an Angular template—login forms, contact forms, and pretty much any business form. +You can lay out the controls creatively and bind them to the data in your object model. +You can specify validation rules and display validation errors, conditionally enable or disable specific controls, trigger built-in visual feedback, and much more. -Angular makes the process easy by handling many of the repetitive, boilerplate tasks you'd -otherwise wrestle with yourself. +This tutorial shows you how to build a form from scratch, using a simplified sample form like the one from the [Tour of Heroes tutorial](tutorial "Tour of Heroes") to illustrate the techniques. -You'll learn to build a template-driven form that looks like this: +
+ + Run or download the example app: . + +
+ +## Objectives + +This tutorial teaches you how to do the following: + +* Build an Angular form with a component and template. +* Use `ngModel` to create two-way data bindings for reading and writing input-control values. +* Provide visual feedback using special CSS classes that track the state of the controls. +* Display validation errors to users and enable or disable form controls based on the form status. +* Share information across HTML elements using [template reference variables](guide/template-syntax#template-reference-variables-var). + +## Prerequisites + +Before going further into template-driven forms, you should have a basic understanding of the following. + +* TypeScript and HTML5 programming. +* Angular app-design fundamentals, as described in [Angular Concepts](guide/architecture "Introduction to Angular concepts."). +* The basics of [Angular template syntax](guide/template-syntax "Template syntax guide"). +* The form-design concepts that are presented in [Introduction to Forms](guide/forms-overview "Overview of Angular forms."). + +{@a intro} + +## Build a template-driven form + +Template-driven forms rely on directives defined in the `FormsModule`. + +* The `NgModel` directive reconciles value changes in the attached form element with changes in the data model, allowing you to respond to user input with input validation and error handling. + +* The `NgForm` directive creates a top-level `FormGroup` instance and binds it to a `
` element to track aggregated form value and validation status. +As soon as you import `FormsModule`, this directive becomes active by default on all `` tags. You don't need to add a special selector. + +* The `NgModelGroup` directive creates and binds a `FormGroup` instance to a DOM element. + +### The sample application + +The sample form in this guide is used by the *Hero Employment Agency* to maintain personal information about heroes. +Every hero needs a job. This form helps the agency match the right hero with the right crisis. -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. +The form highlights some design features that make it easier to use. For instance, the two required fields have a green bar on the left to make them easy to spot. These fields have initial values, so the form is valid and the **Submit** button is enabled. -Two of the three fields on this form are required. Required fields have a green bar on the left to make them easy to spot. - -If you delete the hero name, the form displays a validation error in an attention-grabbing style: +As you work with this form, you will learn how to include validation logic, how to customize the presentation with standard CSS, and how to handle error conditions to ensure valid input. +If the user deletes the hero name, for example, the form becomes invalid. The app detects the changed status, and displays a validation error in an attention-grabbing style. +In addition, 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. +### Step overview -
+In the course of this tutorial, you bind a sample form to data and handle user input using the following steps. - You can customize the colors and location of the "required" bar with standard CSS. +1. Build the basic form. + * Define a sample data model. + * Include required infrastructure such as the `FormsModule`. +2. Bind form controls to data properties using the `ngModel` directive and two-way data-binding syntax. + * Examine how `ngModel` reports control states using CSS classes. + * Name controls to make them accessible to `ngModel`. +3. Track input validity and control status using `ngModel`. + * Add custom CSS to provide visual feedback on the status. + * Show and hide validation-error messages. +4. Respond to a native HTML button-click event by adding to the model data. +5. Handle form submission using the [`ngSubmit`(api/forms/NgForm#properties)] output property of the form. + * Disable the **Submit** button until the form is valid. + * After submit, swap out the finished form for different content on the page. -
+{@a step1} -You'll build this form in small steps: +## Build the form -1. Create the `Hero` model class. -1. Create the component that controls the form. -1. Create a template with the initial form layout. -1. Bind data properties to each form control using the `ngModel` two-way data-binding syntax. -1. Add a `name` attribute to each form-input control. -1. Add custom CSS to provide visual feedback. -1. Show and hide validation-error messages. -1. Handle form submission with *ngSubmit*. -1. Disable the form’s *Submit* button until the form is valid. +You can recreate the sample application from the code provided here, or you can examine or download the . -## Setup +1. The provided sample application creates the `Hero` class which defines the data model reflected in the form. -Create a new project named angular-forms: + - +2. The form layout and details are defined in the `HeroFormComponent` class. - ng new angular-forms + - + The component's `selector` value of "app-hero-form" means you can drop this form in a parent +template using the `` tag. -## Create the Hero model class +3. The following code creates a new hero instance, so that the initial form can show an example hero. -As users enter form data, you'll capture their changes and update an instance of a model. -You can't lay out the form until you know what the model looks like. + -A model can be as simple as a "property bag" that holds facts about a thing of application importance. -That describes well the `Hero` class with its three required fields (`id`, `name`, `power`) -and one optional field (`alterEgo`). + This demo uses dummy data for `model` and `powers`. In a real app, you would inject a data service to get and save real data, or expose these properties as inputs and outputs. -Using the Angular CLI command [`ng generate class`](cli/generate), generate a new class named `Hero`: +4. The application enables the Forms feature and registers the created form component. - + - ng generate class Hero +5. The form is displayed in the application layout defined by the root component's template. - + -With this content: + The initial template defines the layout for a form with two form groups and a submit button. + The form groups correspond to two properties of the Hero data model, name and alterEgo. Each group has a label and a box for user input. - + * The **Name** `` control element has the HTML5 `required` attribute. + * The **Alter Ego** `` control element does not because `alterEgo` is optional. -It's an anemic model with few requirements and no behavior. Perfect for the demo. + The **Submit** button has some classes on it for styling. + At this point, the form layout is all plain HTML5, with no bindings or directives. -The TypeScript compiler generates a public field for each `public` constructor parameter and -automatically assigns the parameter’s value to that field when you create heroes. +6. The sample form uses some style classes from [Twitter Bootstrap](http://getbootstrap.com/css/): `container`, `form-group`, `form-control`, and `btn`. + To use these styles, the app's style sheet imports the library. -The `alterEgo` is optional, so the constructor lets you omit it; note the question mark (?) in `alterEgo?`. + -You can create a new hero like this: +7. The form makes the hero applicant choose one superpower from a fixed list of agency-approved powers. + The predefined list of `powers` is part of the data model, maintained internally in `HeroFormComponent`. + The Angular [NgForOf directive](api/common/NgForOf "API reference") iterates over the data values to populate the `` control has the HTML5 `required` attribute; -the *Alter Ego* `` control does not because `alterEgo` is optional. - -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. - -
- - In template driven forms, if you've imported `FormsModule`, you don't have to do anything - to the `` tag in order to make use of `FormsModule`. Continue on to see how this works. - -
- -The `container`, `form-group`, `form-control`, and `btn` classes -come from [Twitter Bootstrap](http://getbootstrap.com/css/). These classes are purely cosmetic. -Bootstrap gives the form a little style. - -
- -
- Angular forms don't require a style library -
- - 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. - -
- -To add the stylesheet, open `styles.css` and add the following import line at the top: - - - -## Add powers with _*ngFor_ - -The hero must choose one superpower from a fixed list of agency-approved powers. -You maintain that list internally (in `HeroFormComponent`). - -You'll add a `select` to the -form and bind the options to the `powers` list using `ngFor`, -a technique seen previously in the [Displaying Data](guide/displaying-data) page. - -Add the following HTML *immediately below* the *Alter Ego* group: - - - -This code repeats the `