mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
build: set up markdown pipeline for forms directory of the content guide (#53290)
Set up the markdown pipeline to include the required source files. PR Close #53290
This commit is contained in:
parent
289f76f569
commit
e582d60f36
6 changed files with 159 additions and 103 deletions
52
adev/src/content/guide/forms/BUILD.bazel
Normal file
52
adev/src/content/guide/forms/BUILD.bazel
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
load("@npm//@angular/build-tooling/bazel/markdown_to_html:markdown_to_html.bzl", "markdown_to_html")
|
||||
|
||||
markdown_to_html(
|
||||
name = "forms",
|
||||
srcs = glob([
|
||||
"*.md",
|
||||
]),
|
||||
data = [
|
||||
"//adev/src/assets/images:overview.svg",
|
||||
"//adev/src/content/examples/dynamic-form:src/app/app.component.ts",
|
||||
"//adev/src/content/examples/dynamic-form:src/app/dynamic-form.component.html",
|
||||
"//adev/src/content/examples/dynamic-form:src/app/dynamic-form.component.ts",
|
||||
"//adev/src/content/examples/dynamic-form:src/app/dynamic-form-question.component.html",
|
||||
"//adev/src/content/examples/dynamic-form:src/app/dynamic-form-question.component.ts",
|
||||
"//adev/src/content/examples/dynamic-form:src/app/question.service.ts",
|
||||
"//adev/src/content/examples/dynamic-form:src/app/question-base.ts",
|
||||
"//adev/src/content/examples/dynamic-form:src/app/question-control.service.ts",
|
||||
"//adev/src/content/examples/dynamic-form:src/app/question-dropdown.ts",
|
||||
"//adev/src/content/examples/dynamic-form:src/app/question-textbox.ts",
|
||||
"//adev/src/content/examples/form-validation:src/app/reactive/actor-form-reactive.component.1.ts",
|
||||
"//adev/src/content/examples/form-validation:src/app/reactive/actor-form-reactive.component.2.ts",
|
||||
"//adev/src/content/examples/form-validation:src/app/reactive/actor-form-reactive.component.html",
|
||||
"//adev/src/content/examples/form-validation:src/app/shared/forbidden-name.directive.ts",
|
||||
"//adev/src/content/examples/form-validation:src/app/shared/role.directive.ts",
|
||||
"//adev/src/content/examples/form-validation:src/app/shared/unambiguous-role.directive.ts",
|
||||
"//adev/src/content/examples/form-validation:src/app/template/actor-form-template.component.html",
|
||||
"//adev/src/content/examples/form-validation:src/assets/forms.css",
|
||||
"//adev/src/content/examples/forms:src/app/actor.ts",
|
||||
"//adev/src/content/examples/forms:src/app/actor-form/actor-form.component.html",
|
||||
"//adev/src/content/examples/forms:src/app/actor-form/actor-form.component.ts",
|
||||
"//adev/src/content/examples/forms:src/app/app.component.html",
|
||||
"//adev/src/content/examples/forms:src/app/app.component.ts",
|
||||
"//adev/src/content/examples/forms:src/app/app.module.ts",
|
||||
"//adev/src/content/examples/forms:src/assets/forms.css",
|
||||
"//adev/src/content/examples/forms:src/index.html",
|
||||
"//adev/src/content/examples/forms:src/main.ts",
|
||||
"//adev/src/content/examples/forms:src/styles.1.css",
|
||||
"//adev/src/content/examples/forms-overview:src/app/reactive/favorite-color/favorite-color.component.spec.ts",
|
||||
"//adev/src/content/examples/forms-overview:src/app/reactive/favorite-color/favorite-color.component.ts",
|
||||
"//adev/src/content/examples/forms-overview:src/app/template/favorite-color/favorite-color.component.spec.ts",
|
||||
"//adev/src/content/examples/forms-overview:src/app/template/favorite-color/favorite-color.component.ts",
|
||||
"//adev/src/content/examples/reactive-forms:src/app/app.component.1.html",
|
||||
"//adev/src/content/examples/reactive-forms:src/app/app.module.ts",
|
||||
"//adev/src/content/examples/reactive-forms:src/app/name-editor/name-editor.component.html",
|
||||
"//adev/src/content/examples/reactive-forms:src/app/name-editor/name-editor.component.ts",
|
||||
"//adev/src/content/examples/reactive-forms:src/app/profile-editor/profile-editor.component.1.html",
|
||||
"//adev/src/content/examples/reactive-forms:src/app/profile-editor/profile-editor.component.1.ts",
|
||||
"//adev/src/content/examples/reactive-forms:src/app/profile-editor/profile-editor.component.2.ts",
|
||||
"//adev/src/content/examples/reactive-forms:src/app/profile-editor/profile-editor.component.html",
|
||||
"//adev/src/content/examples/reactive-forms:src/app/profile-editor/profile-editor.component.ts",
|
||||
],
|
||||
)
|
||||
|
|
@ -35,8 +35,8 @@ To give the application access reactive forms directives, import `ReactiveFormsM
|
|||
The following code from the example shows the setup in the root module.
|
||||
|
||||
<docs-code-multifile>
|
||||
<docs-code header="dynamic-form.component.ts" path="dynamic-form/src/app/dynamic-form.component.ts"/>
|
||||
<docs-code header="dynamic-form-question.component.ts" path="dynamic-form/src/app/dynamic-form-question.component.ts"/>
|
||||
<docs-code header="dynamic-form.component.ts" path="adev/src/content/examples/dynamic-form/src/app/dynamic-form.component.ts"/>
|
||||
<docs-code header="dynamic-form-question.component.ts" path="adev/src/content/examples/dynamic-form/src/app/dynamic-form-question.component.ts"/>
|
||||
</docs-code-multifile>
|
||||
|
||||
## Create a form object model
|
||||
|
|
@ -49,7 +49,7 @@ The example includes the `DynamicFormQuestionComponent`, which defines a questio
|
|||
|
||||
The following `QuestionBase` is a base class for a set of controls that can represent the question and its answer in the form.
|
||||
|
||||
<docs-code header="src/app/question-base.ts" path="dynamic-form/src/app/question-base.ts"/>
|
||||
<docs-code header="src/app/question-base.ts" path="adev/src/content/examples/dynamic-form/src/app/question-base.ts"/>
|
||||
|
||||
### Define control classes
|
||||
|
||||
|
|
@ -58,11 +58,11 @@ When you create the form template in the next step, you instantiate these specif
|
|||
|
||||
The `TextboxQuestion` control type is represented in a form template using an `<input>` element. It presents a question and lets users enter input. The `type` attribute of the element is defined based on the `type` field specified in the `options` argument (for example `text`, `email`, `url`).
|
||||
|
||||
<docs-code header="question-textbox.ts" path="dynamic-form/src/app/question-textbox.ts"/>
|
||||
<docs-code header="question-textbox.ts" path="adev/src/content/examples/dynamic-form/src/app/question-textbox.ts"/>
|
||||
|
||||
The `DropdownQuestion` control type presents a list of choices in a select box.
|
||||
|
||||
<docs-code header="question-dropdown.ts" path="dynamic-form/src/app/question-dropdown.ts"/>
|
||||
<docs-code header="question-dropdown.ts" path="adev/src/content/examples/dynamic-form/src/app/question-dropdown.ts"/>
|
||||
|
||||
### Compose form groups
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ A dynamic form uses a service to create grouped sets of input controls, based on
|
|||
The following `QuestionControlService` collects a set of `FormGroup` instances that consume the metadata from the question model.
|
||||
You can specify default values and validation rules.
|
||||
|
||||
<docs-code header="src/app/question-control.service.ts" path="dynamic-form/src/app/question-control.service.ts"/>
|
||||
<docs-code header="src/app/question-control.service.ts" path="adev/src/content/examples/dynamic-form/src/app/question-control.service.ts"/>
|
||||
|
||||
## Compose dynamic form contents
|
||||
|
||||
|
|
@ -82,8 +82,8 @@ The form relies on a [`[formGroup]` directive](api/forms/FormGroupDirective "API
|
|||
The `DynamicFormQuestionComponent` creates form groups and populates them with controls defined in the question model, specifying display and validation rules.
|
||||
|
||||
<docs-code-multifile>
|
||||
<docs-code header="dynamic-form-question.component.html" path="dynamic-form/src/app/dynamic-form-question.component.html"/>
|
||||
<docs-code header="dynamic-form-question.component.ts" path="dynamic-form/src/app/dynamic-form-question.component.ts"/>
|
||||
<docs-code header="dynamic-form-question.component.html" path="adev/src/content/examples/dynamic-form/src/app/dynamic-form-question.component.html"/>
|
||||
<docs-code header="dynamic-form-question.component.ts" path="adev/src/content/examples/dynamic-form/src/app/dynamic-form-question.component.ts"/>
|
||||
</docs-code-multifile>
|
||||
|
||||
The goal of the `DynamicFormQuestionComponent` is to present question types defined in your model.
|
||||
|
|
@ -102,7 +102,7 @@ To maintain the questionnaire as requirements change, you only need to add, upda
|
|||
|
||||
The `QuestionService` supplies a set of questions in the form of an array bound to `@Input()` questions.
|
||||
|
||||
<docs-code header="src/app/question.service.ts" path="dynamic-form/src/app/question.service.ts"/>
|
||||
<docs-code header="src/app/question.service.ts" path="adev/src/content/examples/dynamic-form/src/app/question.service.ts"/>
|
||||
|
||||
## Create a dynamic form template
|
||||
|
||||
|
|
@ -111,15 +111,15 @@ The `DynamicFormComponent` component is the entry point and the main container f
|
|||
The `DynamicFormComponent` component presents a list of questions by binding each one to an `<app-question>` element that matches the `DynamicFormQuestionComponent`.
|
||||
|
||||
<docs-code-multifile>
|
||||
<docs-code header="dynamic-form.component.html" path="dynamic-form/src/app/dynamic-form.component.html"/>
|
||||
<docs-code header="dynamic-form.component.ts" path="dynamic-form/src/app/dynamic-form.component.ts"/>
|
||||
<docs-code header="dynamic-form.component.html" path="adev/src/content/examples/dynamic-form/src/app/dynamic-form.component.html"/>
|
||||
<docs-code header="dynamic-form.component.ts" path="adev/src/content/examples/dynamic-form/src/app/dynamic-form.component.ts"/>
|
||||
</docs-code-multifile>
|
||||
|
||||
### Display the form
|
||||
|
||||
To display an instance of the dynamic form, the `AppComponent` shell template passes the `questions` array returned by the `QuestionService` to the form container component, `<app-dynamic-form>`.
|
||||
|
||||
<docs-code header="app.component.ts" path="dynamic-form/src/app/app.component.ts"/>
|
||||
<docs-code header="app.component.ts" path="adev/src/content/examples/dynamic-form/src/app/app.component.ts"/>
|
||||
|
||||
This separation of model and data lets you repurpose the components for any type of survey, as long as it's compatible with the _question_ object model.
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ Every time the value of a form control changes, Angular runs validation and gene
|
|||
You can then inspect the control's state by exporting `ngModel` to a local template variable.
|
||||
The following example exports `NgModel` into a variable called `name`:
|
||||
|
||||
<docs-code header="template/actor-form-template.component.html (name)" path="form-validation/src/app/template/actor-form-template.component.html" visibleRegion="name-with-error-msg"/>
|
||||
<docs-code header="template/actor-form-template.component.html (name)" path="adev/src/content/examples/form-validation/src/app/template/actor-form-template.component.html" visibleRegion="name-with-error-msg"/>
|
||||
|
||||
Notice the following features illustrated by the example.
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ For a full list of built-in validators, see the [Validators](api/forms/Validator
|
|||
To update the actor form to be a reactive form, use some of the same
|
||||
built-in validators —this time, in function form, as in the following example.
|
||||
|
||||
<docs-code header="reactive/actor-form-reactive.component.ts (validator functions)" path="form-validation/src/app/reactive/actor-form-reactive.component.1.ts" visibleRegion="form-group"/>
|
||||
<docs-code header="reactive/actor-form-reactive.component.ts (validator functions)" path="adev/src/content/examples/form-validation/src/app/reactive/actor-form-reactive.component.1.ts" visibleRegion="form-group"/>
|
||||
|
||||
In this example, the `name` control sets up two built-in validators —`Validators.required` and `Validators.minLength(4)`— and one custom validator, `forbiddenNameValidator`.
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ In a reactive form, you can always access any form control through the `get` met
|
|||
|
||||
If you look at the template for the `name` input again, it is fairly similar to the template-driven example.
|
||||
|
||||
<docs-code header="reactive/actor-form-reactive.component.html (name with error msg)" path="form-validation/src/app/reactive/actor-form-reactive.component.html" visibleRegion="name-with-error-msg"/>
|
||||
<docs-code header="reactive/actor-form-reactive.component.html (name with error msg)" path="adev/src/content/examples/form-validation/src/app/reactive/actor-form-reactive.component.html" visibleRegion="name-with-error-msg"/>
|
||||
|
||||
This form differs from the template-driven version in that it no longer exports any directives. Instead, it uses the `name` getter defined in the component class.
|
||||
|
||||
|
|
@ -88,7 +88,7 @@ The built-in validators don't always match the exact use case of your applicatio
|
|||
Consider the `forbiddenNameValidator` function from the previous example.
|
||||
Here's what the definition of that function looks like.
|
||||
|
||||
<docs-code header="shared/forbidden-name.directive.ts (forbiddenNameValidator)" path="form-validation/src/app/shared/forbidden-name.directive.ts" visibleRegion="custom-validator"/>
|
||||
<docs-code header="shared/forbidden-name.directive.ts (forbiddenNameValidator)" path="adev/src/content/examples/form-validation/src/app/shared/forbidden-name.directive.ts" visibleRegion="custom-validator"/>
|
||||
|
||||
The function is a factory that takes a regular expression to detect a *specific* forbidden name and returns a validator function.
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ In the case of an observable, the observable must complete, at which point the f
|
|||
|
||||
In reactive forms, add a custom validator by passing the function directly to the `FormControl`.
|
||||
|
||||
<docs-code header="reactive/actor-form-reactive.component.ts (validator functions)" path="form-validation/src/app/reactive/actor-form-reactive.component.1.ts" visibleRegion="custom-validator"/>
|
||||
<docs-code header="reactive/actor-form-reactive.component.ts (validator functions)" path="adev/src/content/examples/form-validation/src/app/reactive/actor-form-reactive.component.1.ts" visibleRegion="custom-validator"/>
|
||||
|
||||
### Adding custom validators to template-driven forms
|
||||
|
||||
|
|
@ -116,17 +116,17 @@ For example, the corresponding `ForbiddenValidatorDirective` serves as a wrapper
|
|||
Angular recognizes the directive's role in the validation process because the directive registers itself with the `NG_VALIDATORS` provider, as shown in the following example.
|
||||
`NG_VALIDATORS` is a predefined provider with an extensible collection of validators.
|
||||
|
||||
<docs-code header="shared/forbidden-name.directive.ts (providers)" path="form-validation/src/app/shared/forbidden-name.directive.ts" visibleRegion="directive-providers"/>
|
||||
<docs-code header="shared/forbidden-name.directive.ts (providers)" path="adev/src/content/examples/form-validation/src/app/shared/forbidden-name.directive.ts" visibleRegion="directive-providers"/>
|
||||
|
||||
The directive class then implements the `Validator` interface, so that it can easily integrate with Angular forms.
|
||||
Here is the rest of the directive to help you get an idea of how it all comes together.
|
||||
|
||||
<docs-code header="shared/forbidden-name.directive.ts (directive)" path="form-validation/src/app/shared/forbidden-name.directive.ts" visibleRegion="directive"/>
|
||||
<docs-code header="shared/forbidden-name.directive.ts (directive)" path="adev/src/content/examples/form-validation/src/app/shared/forbidden-name.directive.ts" visibleRegion="directive"/>
|
||||
|
||||
Once the `ForbiddenValidatorDirective` is ready, you can add its selector, `appForbiddenName`, to any input element to activate it.
|
||||
For example:
|
||||
|
||||
<docs-code header="template/actor-form-template.component.html (forbidden-name-input)" path="form-validation/src/app/template/actor-form-template.component.html" visibleRegion="name-input"/>
|
||||
<docs-code header="template/actor-form-template.component.html (forbidden-name-input)" path="adev/src/content/examples/form-validation/src/app/template/actor-form-template.component.html" visibleRegion="name-input"/>
|
||||
|
||||
HELPFUL: Notice that the custom validation directive is instantiated with `useExisting` rather than `useClass`.
|
||||
The registered validator must be *this instance* of the `ForbiddenValidatorDirective` —the instance in the form with its `forbiddenName` property bound to "bob".
|
||||
|
|
@ -151,7 +151,7 @@ The following classes are currently supported.
|
|||
In the following example, the actor form uses the `.ng-valid` and `.ng-invalid` classes to
|
||||
set the color of each form control's border.
|
||||
|
||||
<docs-code header="forms.css (status classes)" path="form-validation/src/assets/forms.css"/>
|
||||
<docs-code header="forms.css (status classes)" path="adev/src/content/examples/form-validation/src/assets/forms.css"/>
|
||||
|
||||
## Cross-field validation
|
||||
|
||||
|
|
@ -199,7 +199,7 @@ const actorForm = new FormGroup({
|
|||
|
||||
The validator code is as follows.
|
||||
|
||||
<docs-code header="shared/unambiguous-role.directive.ts" path="form-validation/src/app/shared/unambiguous-role.directive.ts" visibleRegion="cross-validation-validator"/>
|
||||
<docs-code header="shared/unambiguous-role.directive.ts" path="adev/src/content/examples/form-validation/src/app/shared/unambiguous-role.directive.ts" visibleRegion="cross-validation-validator"/>
|
||||
|
||||
The `unambiguousRoleValidator` validator implements the `ValidatorFn` interface.
|
||||
It takes an Angular control object as an argument and returns either null if the form is valid, or `ValidationErrors` otherwise.
|
||||
|
|
@ -211,7 +211,7 @@ If they do match, the actor's role is ambiguous and the validator must mark the
|
|||
|
||||
To provide better user experience, the template shows an appropriate error message when the form is invalid.
|
||||
|
||||
<docs-code header="reactive/actor-form-template.component.html" path="form-validation/src/app/reactive/actor-form-reactive.component.html" visibleRegion="cross-validation-error-message"/>
|
||||
<docs-code header="reactive/actor-form-template.component.html" path="adev/src/content/examples/form-validation/src/app/reactive/actor-form-reactive.component.html" visibleRegion="cross-validation-error-message"/>
|
||||
|
||||
This `*ngIf` displays the error if the `FormGroup` has the cross validation error returned by the `unambiguousRoleValidator` validator, but only if the user finished [interacting with the form](#control-status-css-classes).
|
||||
|
||||
|
|
@ -220,16 +220,16 @@ This `*ngIf` displays the error if the `FormGroup` has the cross validation erro
|
|||
For a template-driven form, you must create a directive to wrap the validator function.
|
||||
You provide that directive as the validator using the [`NG_VALIDATORS` token](/api/forms/NG_VALIDATORS), as shown in the following example.
|
||||
|
||||
<docs-code header="shared/unambiguous-role.directive.ts" path="form-validation/src/app/shared/unambiguous-role.directive.ts" visibleRegion="cross-validation-directive"/>
|
||||
<docs-code header="shared/unambiguous-role.directive.ts" path="adev/src/content/examples/form-validation/src/app/shared/unambiguous-role.directive.ts" visibleRegion="cross-validation-directive"/>
|
||||
|
||||
You must add the new directive to the HTML template.
|
||||
Because the validator must be registered at the highest level in the form, the following template puts the directive on the `form` tag.
|
||||
|
||||
<docs-code header="template/actor-form-template.component.html" path="form-validation/src/app/template/actor-form-template.component.html" visibleRegion="cross-validation-register-validator"/>
|
||||
<docs-code header="template/actor-form-template.component.html" path="adev/src/content/examples/form-validation/src/app/template/actor-form-template.component.html" visibleRegion="cross-validation-register-validator"/>
|
||||
|
||||
To provide better user experience, an appropriate error message appears when the form is invalid.
|
||||
|
||||
<docs-code header="template/actor-form-template.component.html" path="form-validation/src/app/template/actor-form-template.component.html" visibleRegion="cross-validation-error-message"/>
|
||||
<docs-code header="template/actor-form-template.component.html" path="adev/src/content/examples/form-validation/src/app/template/actor-form-template.component.html" visibleRegion="cross-validation-error-message"/>
|
||||
|
||||
This is the same in both template-driven and reactive forms.
|
||||
|
||||
|
|
@ -266,7 +266,7 @@ To validate the potential role entry, the validator must initiate an asynchronou
|
|||
|
||||
The following code creates the validator class, `UniqueRoleValidator`, which implements the `AsyncValidator` interface.
|
||||
|
||||
<docs-code path="form-validation/src/app/shared/role.directive.ts" visibleRegion="async-validator"/>
|
||||
<docs-code path="adev/src/content/examples/form-validation/src/app/shared/role.directive.ts" visibleRegion="async-validator"/>
|
||||
|
||||
The constructor injects the `ActorsService`, which defines the following interface.
|
||||
|
||||
|
|
@ -297,7 +297,7 @@ The `pending` flag is set to `false`, and the form validity is updated.
|
|||
|
||||
To use an async validator in reactive forms, begin by injecting the validator into the constructor of the component class.
|
||||
|
||||
<docs-code path="form-validation/src/app/reactive/actor-form-reactive.component.2.ts" visibleRegion="async-validator-inject"/>
|
||||
<docs-code path="adev/src/content/examples/form-validation/src/app/reactive/actor-form-reactive.component.2.ts" visibleRegion="async-validator-inject"/>
|
||||
|
||||
Then, pass the validator function directly to the `FormControl` to apply it.
|
||||
|
||||
|
|
@ -305,7 +305,7 @@ In the following example, the `validate` function of `UnambiguousRoleValidator`
|
|||
The value of `asyncValidators` can be either a single async validator function, or an array of functions.
|
||||
To learn more about `FormControl` options, see the [AbstractControlOptions](api/forms/AbstractControlOptions) API reference.
|
||||
|
||||
<docs-code path="form-validation/src/app/reactive/actor-form-reactive.component.2.ts" visibleRegion="async-validator-usage"/>
|
||||
<docs-code path="adev/src/content/examples/form-validation/src/app/reactive/actor-form-reactive.component.2.ts" visibleRegion="async-validator-usage"/>
|
||||
|
||||
### Adding async validators to template-driven forms
|
||||
|
||||
|
|
@ -313,11 +313,11 @@ To use an async validator in template-driven forms, create a new directive and r
|
|||
|
||||
In the example below, the directive injects the `UniqueRoleValidator` class that contains the actual validation logic and invokes it in the `validate` function, triggered by Angular when validation should happen.
|
||||
|
||||
<docs-code path="form-validation/src/app/shared/role.directive.ts" visibleRegion="async-validator-directive"/>
|
||||
<docs-code path="adev/src/content/examples/form-validation/src/app/shared/role.directive.ts" visibleRegion="async-validator-directive"/>
|
||||
|
||||
Then, as with synchronous validators, add the directive's selector to an input to activate it.
|
||||
|
||||
<docs-code header="template/actor-form-template.component.html (unique-unambiguous-role-input)" path="form-validation/src/app/template/actor-form-template.component.html" visibleRegion="role-input"/>
|
||||
<docs-code header="template/actor-form-template.component.html (unique-unambiguous-role-input)" path="adev/src/content/examples/form-validation/src/app/template/actor-form-template.component.html" visibleRegion="role-input"/>
|
||||
|
||||
### Optimizing performance of async validators
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<docs-decorative-header title="Forms in Angular" imgSrc="assets/images/overview.svg"> <!-- markdownlint-disable-line -->
|
||||
<docs-decorative-header title="Forms in Angular" imgSrc="adev/src/assets/images/overview.svg"> <!-- markdownlint-disable-line -->
|
||||
Handling user input with forms is the cornerstone of many common applications.
|
||||
</docs-decorative-header>
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ The `[formControl]` directive links the explicitly created `FormControl` instanc
|
|||
The following component implements an input field for a single control, using reactive forms.
|
||||
In this example, the form model is the `FormControl` instance.
|
||||
|
||||
<docs-code path="forms-overview/src/app/reactive/favorite-color/favorite-color.component.ts"/>
|
||||
<docs-code path="adev/src/content/examples/forms-overview/src/app/reactive/favorite-color/favorite-color.component.ts"/>
|
||||
|
||||
IMPORTANT: In reactive forms, the form model is the source of truth; it provides the value and status of the form element at any given point in time, through the `[formControl]` directive on the `<input>` element.
|
||||
|
||||
|
|
@ -81,7 +81,7 @@ The directive `NgModel` creates and manages a `FormControl` instance for a given
|
|||
|
||||
The following component implements the same input field for a single control, using template-driven forms.
|
||||
|
||||
<docs-code path="forms-overview/src/app/template/favorite-color/favorite-color.component.ts"/>
|
||||
<docs-code path="adev/src/content/examples/forms-overview/src/app/template/favorite-color/favorite-color.component.ts"/>
|
||||
|
||||
IMPORTANT: In a template-driven form the source of truth is the template. The `NgModel` directive automatically manages the `FormControl` instance for you.
|
||||
|
||||
|
|
@ -107,7 +107,8 @@ The view-to-model diagram shows how data flows when an input field's value is ch
|
|||
1. The `FormControl` instance emits the new value through the `valueChanges` observable.
|
||||
1. Any subscribers to the `valueChanges` observable receive the new value.
|
||||
|
||||
```mermaid
|
||||
<!-- TODO(josephperrott): enable this mermaid chart -->
|
||||
```
|
||||
flowchart TB
|
||||
U{User}
|
||||
I("<input>")
|
||||
|
|
@ -127,7 +128,8 @@ The model-to-view diagram shows how a programmatic change to the model is propag
|
|||
1. Any subscribers to the `valueChanges` observable receive the new value.
|
||||
1. The control value accessor on the form input element updates the element with the new value.
|
||||
|
||||
```mermaid
|
||||
<!-- TODO(josephperrott): enable this mermaid chart -->
|
||||
```
|
||||
flowchart TB
|
||||
U{User}
|
||||
I(<input>)
|
||||
|
|
@ -154,7 +156,8 @@ The view-to-model diagram shows how data flows when an input field's value is ch
|
|||
1. The control value accessor also calls the `NgModel.viewToModelUpdate()` method which emits an `ngModelChange` event.
|
||||
1. Because the component template uses two-way data binding for the `favoriteColor` property, the `favoriteColor` property in the component is updated to the value emitted by the `ngModelChange` event \(*Blue*\).
|
||||
|
||||
```mermaid
|
||||
<!-- TODO(josephperrott): enable this mermaid chart -->
|
||||
```
|
||||
flowchart TB
|
||||
U{User}
|
||||
I(<input>)
|
||||
|
|
@ -185,7 +188,8 @@ The model-to-view diagram shows how data flows from model to view when the `favo
|
|||
1. Any subscribers to the `valueChanges` observable receive the new value.
|
||||
1. The control value accessor updates the form input element in the view with the latest `favoriteColor` value.
|
||||
|
||||
```mermaid
|
||||
<!-- TODO(josephperrott): enable this mermaid chart -->
|
||||
```
|
||||
flowchart TB
|
||||
C(Component)
|
||||
P(Property bound to NgModel)
|
||||
|
|
@ -266,7 +270,7 @@ The first example performs the following steps to verify the view-to-model data
|
|||
1. Set the new value for the input to *Red*, and dispatch the "input" event on the form input element.
|
||||
1. Assert that the component's `favoriteColorControl` value matches the value from the input.
|
||||
|
||||
<docs-code header="Favorite color test - view to model" path="forms-overview/src/app/reactive/favorite-color/favorite-color.component.spec.ts" visibleRegion="view-to-model"/>
|
||||
<docs-code header="Favorite color test - view to model" path="adev/src/content/examples/forms-overview/src/app/reactive/favorite-color/favorite-color.component.spec.ts" visibleRegion="view-to-model"/>
|
||||
|
||||
The next example performs the following steps to verify the model-to-view data flow.
|
||||
|
||||
|
|
@ -274,7 +278,7 @@ The next example performs the following steps to verify the model-to-view data f
|
|||
1. Query the view for the form input element.
|
||||
1. Assert that the new value set on the control matches the value in the input.
|
||||
|
||||
<docs-code header="Favorite color test - model to view" path="forms-overview/src/app/reactive/favorite-color/favorite-color.component.spec.ts" visibleRegion="model-to-view"/>
|
||||
<docs-code header="Favorite color test - model to view" path="adev/src/content/examples/forms-overview/src/app/reactive/favorite-color/favorite-color.component.spec.ts" visibleRegion="model-to-view"/>
|
||||
|
||||
### Testing template-driven forms
|
||||
|
||||
|
|
@ -284,7 +288,7 @@ The following tests use the favorite color components mentioned earlier to verif
|
|||
|
||||
The following test verifies the data flow from view to model.
|
||||
|
||||
<docs-code header="Favorite color test - view to model" path="forms-overview/src/app/template/favorite-color/favorite-color.component.spec.ts" visibleRegion="view-to-model"/>
|
||||
<docs-code header="Favorite color test - view to model" path="adev/src/content/examples/forms-overview/src/app/template/favorite-color/favorite-color.component.spec.ts" visibleRegion="view-to-model"/>
|
||||
|
||||
Here are the steps performed in the view to model test.
|
||||
|
||||
|
|
@ -295,7 +299,7 @@ Here are the steps performed in the view to model test.
|
|||
|
||||
The following test verifies the data flow from model to view.
|
||||
|
||||
<docs-code header="Favorite color test - model to view" path="forms-overview/src/app/template/favorite-color/favorite-color.component.spec.ts" visibleRegion="model-to-view"/>
|
||||
<docs-code header="Favorite color test - model to view" path="adev/src/content/examples/forms-overview/src/app/template/favorite-color/favorite-color.component.spec.ts" visibleRegion="model-to-view"/>
|
||||
|
||||
Here are the steps performed in the model to view test.
|
||||
|
||||
|
|
|
|||
|
|
@ -38,13 +38,13 @@ In the example, the user enters their name into an input field, captures that in
|
|||
<docs-step title="Import the ReactiveFormsModule">
|
||||
To use reactive form controls, import `ReactiveFormsModule` from the `@angular/forms` package and add it to your NgModule's `imports` array.
|
||||
|
||||
<docs-code header="src/app/app.module.ts (excerpt)" path="reactive-forms/src/app/app.module.ts" visibleRegion="imports" />
|
||||
<docs-code header="src/app/app.module.ts (excerpt)" path="adev/src/content/examples/reactive-forms/src/app/app.module.ts" visibleRegion="imports" />
|
||||
</docs-step>
|
||||
|
||||
<docs-step title="Generate a new component with a FormControl">
|
||||
Use the CLI command `ng generate component` to generate a component in your project to host the control.
|
||||
|
||||
<docs-code header="src/app/name-editor/name-editor.component.ts" path="reactive-forms/src/app/name-editor/name-editor.component.ts" visibleRegion="create-control"/>
|
||||
<docs-code header="src/app/name-editor/name-editor.component.ts" path="adev/src/content/examples/reactive-forms/src/app/name-editor/name-editor.component.ts" visibleRegion="create-control"/>
|
||||
|
||||
Use the constructor of `FormControl` to set its initial value, which in this case is an empty string. By creating these controls in your component class, you get immediate access to listen for, update, and validate the state of the form input.
|
||||
</docs-step>
|
||||
|
|
@ -52,7 +52,7 @@ Use the constructor of `FormControl` to set its initial value, which in this cas
|
|||
<docs-step title="Register the control in the template">
|
||||
After you create the control in the component class, you must associate it with a form control element in the template. Update the template with the form control using the `formControl` binding provided by `FormControlDirective`, which is also included in the `ReactiveFormsModule`.
|
||||
|
||||
<docs-code header="src/app/name-editor/name-editor.component.html" path="reactive-forms/src/app/name-editor/name-editor.component.html" visibleRegion="control-binding" />
|
||||
<docs-code header="src/app/name-editor/name-editor.component.html" path="adev/src/content/examples/reactive-forms/src/app/name-editor/name-editor.component.html" visibleRegion="control-binding" />
|
||||
|
||||
Using the template binding syntax, the form control is now registered to the `name` input element in the template. The form control and DOM element communicate with each other: the view reflects changes in the model, and the model reflects changes in the view.
|
||||
</docs-step>
|
||||
|
|
@ -60,7 +60,7 @@ Using the template binding syntax, the form control is now registered to the `na
|
|||
<docs-step title="Display the component">
|
||||
The `FormControl` assigned to the `name` property is displayed when the `<app-name-editor>` component is added to a template.
|
||||
|
||||
<docs-code header="src/app/app.component.html (name editor)" path="reactive-forms/src/app/app.component.1.html" visibleRegion="app-name-editor"/>
|
||||
<docs-code header="src/app/app.component.html (name editor)" path="adev/src/content/examples/reactive-forms/src/app/app.component.1.html" visibleRegion="app-name-editor"/>
|
||||
</docs-step>
|
||||
</docs-workflow>
|
||||
|
||||
|
|
@ -73,7 +73,7 @@ You can display the value in the following ways.
|
|||
|
||||
The following example shows you how to display the current value using interpolation in the template.
|
||||
|
||||
<docs-code header="src/app/name-editor/name-editor.component.html (control value)" path="reactive-forms/src/app/name-editor/name-editor.component.html" visibleRegion="display-value"/>
|
||||
<docs-code header="src/app/name-editor/name-editor.component.html (control value)" path="adev/src/content/examples/reactive-forms/src/app/name-editor/name-editor.component.html" visibleRegion="display-value"/>
|
||||
|
||||
The displayed value changes as you update the form control element.
|
||||
|
||||
|
|
@ -90,12 +90,12 @@ For example, when retrieving form data from a backend API or service, use the `s
|
|||
|
||||
The following example adds a method to the component class to update the value of the control to *Nancy* using the `setValue()` method.
|
||||
|
||||
<docs-code header="src/app/name-editor/name-editor.component.ts (update value)" path="reactive-forms/src/app/name-editor/name-editor.component.ts" visibleRegion="update-value"/>
|
||||
<docs-code header="src/app/name-editor/name-editor.component.ts (update value)" path="adev/src/content/examples/reactive-forms/src/app/name-editor/name-editor.component.ts" visibleRegion="update-value"/>
|
||||
|
||||
Update the template with a button to simulate a name update.
|
||||
When you click the **Update Name** button, the value entered in the form control element is reflected as its current value.
|
||||
|
||||
<docs-code header="src/app/name-editor/name-editor.component.html (update value)" path="reactive-forms/src/app/name-editor/name-editor.component.html" visibleRegion="update-value"/>
|
||||
<docs-code header="src/app/name-editor/name-editor.component.html (update value)" path="adev/src/content/examples/reactive-forms/src/app/name-editor/name-editor.component.html" visibleRegion="update-value"/>
|
||||
|
||||
The form model is the source of truth for the control, so when you click the button, the value of the input is changed within the component class, overriding its current value.
|
||||
|
||||
|
|
@ -122,7 +122,7 @@ Generate a `ProfileEditor` component and import the `FormGroup` and `FormControl
|
|||
ng generate component ProfileEditor
|
||||
</docs-code>
|
||||
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (imports)" path="reactive-forms/src/app/profile-editor/profile-editor.component.1.ts" visibleRegion="imports"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (imports)" path="adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.1.ts" visibleRegion="imports"/>
|
||||
|
||||
To add a form group to this component, take the following steps.
|
||||
|
||||
|
|
@ -137,7 +137,7 @@ Create a property in the component class named `profileForm` and set the propert
|
|||
|
||||
For the profile form, add two form control instances with the names `firstName` and `lastName`
|
||||
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (form group)" path="reactive-forms/src/app/profile-editor/profile-editor.component.1.ts" visibleRegion="formgroup"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (form group)" path="adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.1.ts" visibleRegion="formgroup"/>
|
||||
|
||||
The individual form controls are now collected within a group. A `FormGroup` instance provides its model value as an object reduced from the values of each control in the group. A form group instance has the same properties (such as `value` and `untouched`) and methods (such as `setValue()`) as a form control instance.
|
||||
</docs-step>
|
||||
|
|
@ -145,7 +145,7 @@ The individual form controls are now collected within a group. A `FormGroup` ins
|
|||
<docs-step title="Associate the FormGroup model and view">
|
||||
A form group tracks the status and changes for each of its controls, so if one of the controls changes, the parent control also emits a new status or value change. The model for the group is maintained from its members. After you define the model, you must update the template to reflect the model in the view.
|
||||
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.html (template form group)" path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" visibleRegion="formgroup"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.html (template form group)" path="adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.1.html" visibleRegion="formgroup"/>
|
||||
|
||||
Just as a form group contains a group of controls, the *profileForm* `FormGroup` is bound to the `form` element with the `FormGroup` directive, creating a communication layer between the model and the form containing the inputs. The `formControlName` input provided by the `FormControlName` directive binds each individual input to the form control defined in `FormGroup`. The form controls communicate with their respective elements. They also communicate changes to the form group instance, which provides the source of truth for the model value.
|
||||
</docs-step>
|
||||
|
|
@ -153,17 +153,17 @@ Just as a form group contains a group of controls, the *profileForm* `FormGroup`
|
|||
<docs-step title="Save form data">
|
||||
The `ProfileEditor` component accepts input from the user, but in a real scenario you want to capture the form value and make available for further processing outside the component. The `FormGroup` directive listens for the `submit` event emitted by the `form` element and emits an `ngSubmit` event that you can bind to a callback function. Add an `ngSubmit` event listener to the `form` tag with the `onSubmit()` callback method.
|
||||
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.html (submit event)" path="reactive-forms/src/app/profile-editor/profile-editor.component.html" visibleRegion="ng-submit"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.html (submit event)" path="adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.html" visibleRegion="ng-submit"/>
|
||||
|
||||
The `onSubmit()` method in the `ProfileEditor` component captures the current value of `profileForm`. Use `EventEmitter` to keep the form encapsulated and to provide the form value outside the component. The following example uses `console.warn` to log a message to the browser console.
|
||||
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (submit method)" path="reactive-forms/src/app/profile-editor/profile-editor.component.ts" visibleRegion="on-submit"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (submit method)" path="adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.ts" visibleRegion="on-submit"/>
|
||||
|
||||
The `submit` event is emitted by the `form` tag using the built-in DOM event. You trigger the event by clicking a button with `submit` type. This lets the user press the **Enter** key to submit the completed form.
|
||||
|
||||
Use a `button` element to add a button to the bottom of the form to trigger the form submission.
|
||||
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.html (submit button)" path="reactive-forms/src/app/profile-editor/profile-editor.component.html" visibleRegion="submit-button"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.html (submit button)" path="adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.html" visibleRegion="submit-button"/>
|
||||
|
||||
The button in the preceding snippet also has a `disabled` binding attached to it to disable the button when `profileForm` is invalid. You aren't performing any validation yet, so the button is always enabled. Basic form validation is covered in the [Validating form input](#validating-form-input) section.
|
||||
</docs-step>
|
||||
|
|
@ -171,7 +171,7 @@ The button in the preceding snippet also has a `disabled` binding attached to it
|
|||
<docs-step title="Display the component">
|
||||
To display the `ProfileEditor` component that contains the form, add it to a component template.
|
||||
|
||||
<docs-code header="src/app/app.component.html (profile editor)" path="reactive-forms/src/app/app.component.1.html" visibleRegion="app-profile-editor"/>
|
||||
<docs-code header="src/app/app.component.html (profile editor)" path="adev/src/content/examples/reactive-forms/src/app/app.component.1.html" visibleRegion="app-profile-editor"/>
|
||||
|
||||
`ProfileEditor` lets you manage the form control instances for the `firstName` and `lastName` controls within the form group instance.
|
||||
|
||||
|
|
@ -195,7 +195,7 @@ A name and address are typical examples of such nested groups, and are used in t
|
|||
<docs-step title="Create a nested group">
|
||||
To create a nested group in `profileForm`, add a nested `address` element to the form group instance.
|
||||
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (nested form group)" path="reactive-forms/src/app/profile-editor/profile-editor.component.1.ts" visibleRegion="nested-formgroup"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (nested form group)" path="adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.1.ts" visibleRegion="nested-formgroup"/>
|
||||
|
||||
In this example, `address group` combines the current `firstName` and `lastName` controls with the new `street`, `city`, `state`, and `zip` controls. Even though the `address` element in the form group is a child of the overall `profileForm` element in the form group, the same rules apply with value and status changes. Changes in status and value from the nested form group propagate to the parent form group, maintaining consistency with the overall model.
|
||||
</docs-step>
|
||||
|
|
@ -203,7 +203,7 @@ In this example, `address group` combines the current `firstName` and `lastName`
|
|||
<docs-step title="Group the nested form in the template">
|
||||
After you update the model in the component class, update the template to connect the form group instance and its input elements. Add the `address` form group containing the `street`, `city`, `state`, and `zip` fields to the `ProfileEditor` template.
|
||||
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.html (template nested form group)" path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" visibleRegion="formgroupname"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.html (template nested form group)" path="adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.1.html" visibleRegion="formgroupname"/>
|
||||
|
||||
The `ProfileEditor` form is displayed as one group, but the model is broken down further to represent the logical grouping areas.
|
||||
|
||||
|
|
@ -227,11 +227,11 @@ The strict checks of the `setValue()` method help catch nesting errors in comple
|
|||
|
||||
In `ProfileEditorComponent`, use the `updateProfile` method with the following example to update the first name and street address for the user.
|
||||
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (patch value)" path="reactive-forms/src/app/profile-editor/profile-editor.component.1.ts" visibleRegion="patch-value"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (patch value)" path="adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.1.ts" visibleRegion="patch-value"/>
|
||||
|
||||
Simulate an update by adding a button to the template to update the user profile on demand.
|
||||
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.html (update value)" path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" visibleRegion="patch-value"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.html (update value)" path="adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.1.html" visibleRegion="patch-value"/>
|
||||
|
||||
When a user clicks the button, the `profileForm` model is updated with new values for `firstName` and `street`. Notice that `street` is provided in an object inside the `address` property.
|
||||
This is necessary because the `patchValue()` method applies the update against the model structure.
|
||||
|
|
@ -254,28 +254,28 @@ The following examples show how to refactor the `ProfileEditor` component to use
|
|||
<docs-step title="Import the FormBuilder class">
|
||||
Import the `FormBuilder` class from the `@angular/forms` package.
|
||||
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (import)" path="reactive-forms/src/app/profile-editor/profile-editor.component.2.ts" visibleRegion="form-builder-imports"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (import)" path="adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.2.ts" visibleRegion="form-builder-imports"/>
|
||||
|
||||
</docs-step>
|
||||
|
||||
<docs-step title="Inject the FormBuilder service">
|
||||
The `FormBuilder` service is an injectable provider that is provided with the reactive forms module. Inject this dependency by adding it to the component constructor.
|
||||
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (constructor)" path="reactive-forms/src/app/profile-editor/profile-editor.component.2.ts" visibleRegion="inject-form-builder"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (constructor)" path="adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.2.ts" visibleRegion="inject-form-builder"/>
|
||||
|
||||
</docs-step>
|
||||
<docs-step title="Generate form controls">
|
||||
The `FormBuilder` service has three methods: `control()`, `group()`, and `array()`. These are factory methods for generating instances in your component classes including form controls, form groups, and form arrays. Use the `group` method to create the `profileForm` controls.
|
||||
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (form builder)" path="reactive-forms/src/app/profile-editor/profile-editor.component.2.ts" visibleRegion="form-builder"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (form builder)" path="adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.2.ts" visibleRegion="form-builder"/>
|
||||
|
||||
In the preceding example, you use the `group()` method with the same object to define the properties in the model. The value for each control name is an array containing the initial value as the first item in the array.
|
||||
|
||||
Tip: You can define the control with just the initial value, but if your controls need sync or async validation, add sync and async validators as the second and third items in the array. Compare using the form builder to creating the instances manually.
|
||||
|
||||
<docs-code-multifile>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (instances)" path="reactive-forms/src/app/profile-editor/profile-editor.component.1.ts" visibleRegion="formgroup-compare"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (form builder)" path="reactive-forms/src/app/profile-editor/profile-editor.component.2.ts" visibleRegion="formgroup-compare"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (instances)" path="adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.1.ts" visibleRegion="formgroup-compare"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (form builder)" path="adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.2.ts" visibleRegion="formgroup-compare"/>
|
||||
</docs-code-multifile>
|
||||
</docs-step>
|
||||
|
||||
|
|
@ -302,13 +302,13 @@ Reactive forms include a set of validator functions for common use cases. These
|
|||
|
||||
Import the `Validators` class from the `@angular/forms` package.
|
||||
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (import)" path="reactive-forms/src/app/profile-editor/profile-editor.component.ts" visibleRegion="validator-imports"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (import)" path="adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.ts" visibleRegion="validator-imports"/>
|
||||
</docs-step>
|
||||
|
||||
<docs-step title="Make a field required">
|
||||
In the `ProfileEditor` component, add the `Validators.required` static method as the second item in the array for the `firstName` control.
|
||||
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (required validator)" path="reactive-forms/src/app/profile-editor/profile-editor.component.ts" visibleRegion="required-validator"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (required validator)" path="adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.ts" visibleRegion="required-validator"/>
|
||||
</docs-step>
|
||||
|
||||
<docs-step title="Display form status">
|
||||
|
|
@ -316,7 +316,7 @@ When you add a required field to the form control, its initial status is invalid
|
|||
|
||||
Display the current status of `profileForm` using interpolation.
|
||||
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.html (display status)" path="reactive-forms/src/app/profile-editor/profile-editor.component.html" visibleRegion="display-status"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.html (display status)" path="adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.html" visibleRegion="display-status"/>
|
||||
|
||||
The **Submit** button is disabled because `profileForm` is invalid due to the required `firstName` form control. After you fill out the `firstName` input, the form becomes valid and the **Submit** button is enabled.
|
||||
|
||||
|
|
@ -343,7 +343,7 @@ The following example shows you how to manage an array of *aliases* in `ProfileE
|
|||
<docs-step title="Import the `FormArray` class">
|
||||
Import the `FormArray` class from `@angular/forms` to use for type information. The `FormBuilder` service is ready to create a `FormArray` instance.
|
||||
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (import)" path="reactive-forms/src/app/profile-editor/profile-editor.component.2.ts" visibleRegion="form-array-imports"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (import)" path="adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.2.ts" visibleRegion="form-array-imports"/>
|
||||
</docs-step>
|
||||
|
||||
<docs-step title="Define a `FormArray` control">
|
||||
|
|
@ -351,7 +351,7 @@ You can initialize a form array with any number of controls, from zero to many,
|
|||
|
||||
Use the `FormBuilder.array()` method to define the array, and the `FormBuilder.control()` method to populate the array with an initial control.
|
||||
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (aliases form array)" path="reactive-forms/src/app/profile-editor/profile-editor.component.ts" visibleRegion="aliases"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (aliases form array)" path="adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.ts" visibleRegion="aliases"/>
|
||||
|
||||
The aliases control in the form group instance is now populated with a single control until more controls are added dynamically.
|
||||
</docs-step>
|
||||
|
|
@ -361,11 +361,11 @@ A getter provides access to the aliases in the form array instance compared to r
|
|||
|
||||
Use the getter syntax to create an `aliases` class property to retrieve the alias's form array control from the parent form group.
|
||||
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (aliases getter)" path="reactive-forms/src/app/profile-editor/profile-editor.component.ts" visibleRegion="aliases-getter"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (aliases getter)" path="adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.ts" visibleRegion="aliases-getter"/>
|
||||
|
||||
Because the returned control is of the type `AbstractControl`, you need to provide an explicit type to access the method syntax for the form array instance. Define a method to dynamically insert an alias control into the alias's form array. The `FormArray.push()` method inserts the control as a new item in the array.
|
||||
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (add alias)" path="reactive-forms/src/app/profile-editor/profile-editor.component.ts" visibleRegion="add-alias"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.ts (add alias)" path="adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.ts" visibleRegion="add-alias"/>
|
||||
|
||||
In the template, each control is displayed as a separate input field.
|
||||
|
||||
|
|
@ -377,7 +377,7 @@ To attach the aliases from your form model, you must add it to the template. Sim
|
|||
|
||||
Add the following template HTML after the `<div>` closing the `formGroupName` element.
|
||||
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.html (aliases form array template)" path="reactive-forms/src/app/profile-editor/profile-editor.component.html" visibleRegion="formarrayname"/>
|
||||
<docs-code header="src/app/profile-editor/profile-editor.component.html (aliases form array template)" path="adev/src/content/examples/reactive-forms/src/app/profile-editor/profile-editor.component.html" visibleRegion="formarrayname"/>
|
||||
|
||||
The `*ngFor` directive iterates over each form control instance provided by the aliases form array instance. Because form array elements are unnamed, you assign the index to the `i` variable and pass it to each control to bind it to the `formControlName` input.
|
||||
|
||||
|
|
|
|||
|
|
@ -59,28 +59,28 @@ In the course of this tutorial, you bind a sample form to data and handle user i
|
|||
|
||||
1. The provided sample application creates the `Actor` class which defines the data model reflected in the form.
|
||||
|
||||
<docs-code header="src/app/actor.ts" language="typescript" path="forms/src/app/actor.ts"/>
|
||||
<docs-code header="src/app/actor.ts" language="typescript" path="adev/src/content/examples/forms/src/app/actor.ts"/>
|
||||
|
||||
1. The form layout and details are defined in the `ActorFormComponent` class.
|
||||
|
||||
<docs-code header="src/app/actor-form/actor-form.component.ts (v1)" path="forms/src/app/actor-form/actor-form.component.ts" visibleRegion="v1"/>
|
||||
<docs-code header="src/app/actor-form/actor-form.component.ts (v1)" path="adev/src/content/examples/forms/src/app/actor-form/actor-form.component.ts" visibleRegion="v1"/>
|
||||
|
||||
The component's `selector` value of "app-actor-form" means you can drop this form in a parent template using the `<app-actor-form>` tag.
|
||||
|
||||
1. The following code creates a new actor instance, so that the initial form can show an example actor.
|
||||
|
||||
<docs-code language="typescript" path="forms/src/app/actor-form/actor-form.component.ts" language="typescript" visibleRegion="Marilyn"/>
|
||||
<docs-code language="typescript" path="adev/src/content/examples/forms/src/app/actor-form/actor-form.component.ts" language="typescript" visibleRegion="Marilyn"/>
|
||||
|
||||
This demo uses dummy data for `model` and `skills`.
|
||||
In a real app, you would inject a data service to get and save real data, or expose these properties as inputs and outputs.
|
||||
|
||||
1. The application enables the Forms feature and registers the created form component.
|
||||
|
||||
<docs-code header="src/app/app.module.ts" language="typescript" path="forms/src/app/app.module.ts"/>
|
||||
<docs-code header="src/app/app.module.ts" language="typescript" path="adev/src/content/examples/forms/src/app/app.module.ts"/>
|
||||
|
||||
1. The form is displayed in the application layout defined by the root component's template.
|
||||
|
||||
<docs-code header="src/app/app.component.html" language="html" path="forms/src/app/app.component.html"/>
|
||||
<docs-code header="src/app/app.component.html" language="html" path="adev/src/content/examples/forms/src/app/app.component.html"/>
|
||||
|
||||
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 Actor data model, name and studio.
|
||||
|
|
@ -95,12 +95,12 @@ In the course of this tutorial, you bind a sample form to data and handle user i
|
|||
1. The sample form uses some style classes from [Twitter Bootstrap](https://getbootstrap.com/css): `container`, `form-group`, `form-control`, and `btn`.
|
||||
To use these styles, the application's style sheet imports the library.
|
||||
|
||||
<docs-code header="src/styles.css" path="forms/src/styles.1.css"/>
|
||||
<docs-code header="src/styles.css" path="adev/src/content/examples/forms/src/styles.1.css"/>
|
||||
|
||||
1. The form requires that an actor's skill is chosen from a predefined list of `skills` maintained internally in `ActorFormComponent`.
|
||||
The Angular [NgForOf directive](api/common/NgForOf "API reference") iterates over the data values to populate the `<select>` element.
|
||||
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (skills)" path="forms/src/app/actor-form/actor-form.component.html" visibleRegion="skills"/>
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (skills)" path="adev/src/content/examples/forms/src/app/actor-form/actor-form.component.html" visibleRegion="skills"/>
|
||||
|
||||
If you run the application right now, you see the list of skills in the selection control.
|
||||
The input elements are not yet bound to data values or events, so they are still blank and have no behavior.
|
||||
|
|
@ -116,7 +116,7 @@ When you include the directive using the syntax for two-way data binding, `[(ng
|
|||
1. Find the `<input>` tag next to the **Name** label.
|
||||
1. Add the `ngModel` directive, using two-way data binding syntax `[(ngModel)]="..."`.
|
||||
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (excerpt)" path="forms/src/app/actor-form/actor-form.component.html" visibleRegion="ngModelName-1"/>
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (excerpt)" path="adev/src/content/examples/forms/src/app/actor-form/actor-form.component.html" visibleRegion="ngModelName-1"/>
|
||||
|
||||
HELPFUL: This example has a temporary diagnostic interpolation after each input tag, `{{model.name}}`, to show the current data value of the corresponding property. The comment reminds you to remove the diagnostic lines when you have finished observing the two-way data binding at work.
|
||||
|
||||
|
|
@ -129,7 +129,7 @@ To get access to the `NgForm` and the overall form status, declare a [template r
|
|||
1. Edit the template file `actor-form.component.html`.
|
||||
1. Update the `<form>` tag with a template reference variable, `#actorForm`, and set its value as follows.
|
||||
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (excerpt)" path="forms/src/app/actor-form/actor-form.component.html" visibleRegion="template-variable"/>
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (excerpt)" path="adev/src/content/examples/forms/src/app/actor-form/actor-form.component.html" visibleRegion="template-variable"/>
|
||||
|
||||
The `actorForm` template variable is now a reference to the `NgForm` directive instance that governs the form as a whole.
|
||||
|
||||
|
|
@ -154,7 +154,7 @@ Any unique value will do, but using a descriptive name is helpful.
|
|||
|
||||
After these revisions, the form template should look like the following:
|
||||
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (excerpt)" path="forms/src/app/actor-form/actor-form.component.html" visibleRegion="ngModel-2"/>
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (excerpt)" path="adev/src/content/examples/forms/src/app/actor-form/actor-form.component.html" visibleRegion="ngModel-2"/>
|
||||
|
||||
You'll notice that:
|
||||
|
||||
|
|
@ -231,11 +231,11 @@ To change the appearance in this way, take the following steps.
|
|||
1. Add these class definitions to a new `forms.css` file.
|
||||
1. Add the new file to the project as a sibling to `index.html`:
|
||||
|
||||
<docs-code header="src/assets/forms.css" language="css" path="forms/src/assets/forms.css"/>
|
||||
<docs-code header="src/assets/forms.css" language="css" path="adev/src/content/examples/forms/src/assets/forms.css"/>
|
||||
|
||||
1. In the `index.html` file, update the `<head>` tag to include the new style sheet.
|
||||
|
||||
<docs-code header="src/index.html (styles)" path="forms/src/index.html" visibleRegion="styles"/>
|
||||
<docs-code header="src/index.html (styles)" path="adev/src/content/examples/forms/src/index.html" visibleRegion="styles"/>
|
||||
|
||||
### Show and hide validation error messages
|
||||
|
||||
|
|
@ -262,12 +262,12 @@ Add a `<div>` that contains a suitable error message.
|
|||
Show or hide the error message by binding properties of the `name` control to the message `<div>` element's `hidden` property.
|
||||
</docs-step>
|
||||
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (hidden-error-msg)" path="forms/src/app/actor-form/actor-form.component.html" visibleRegion="hidden-error-msg"/>
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (hidden-error-msg)" path="adev/src/content/examples/forms/src/app/actor-form/actor-form.component.html" visibleRegion="hidden-error-msg"/>
|
||||
|
||||
<docs-step title="Add a conditional error message to name">
|
||||
Add a conditional error message to the `name` input box, as in the following example.
|
||||
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (excerpt)" path="forms/src/app/actor-form/actor-form.component.html" visibleRegion="name-with-error-msg"/>
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (excerpt)" path="adev/src/content/examples/forms/src/app/actor-form/actor-form.component.html" visibleRegion="name-with-error-msg"/>
|
||||
</docs-step>
|
||||
</docs-workflow>
|
||||
|
||||
|
|
@ -292,11 +292,11 @@ To let form users add a new actor, you will add a **New Actor** button that resp
|
|||
1. In the template, place a "New Actor" `<button>` element at the bottom of the form.
|
||||
1. In the component file, add the actor-creation method to the actor data model.
|
||||
|
||||
<docs-code header="src/app/actor-form/actor-form.component.ts (New Actor method)" path="forms/src/app/actor-form/actor-form.component.ts" visibleRegion="new-actor"/>
|
||||
<docs-code header="src/app/actor-form/actor-form.component.ts (New Actor method)" path="adev/src/content/examples/forms/src/app/actor-form/actor-form.component.ts" visibleRegion="new-actor"/>
|
||||
|
||||
1. Bind the button's click event to a actor-creation method, `newActor()`.
|
||||
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (New Actor button)" path="forms/src/app/actor-form/actor-form.component.html" visibleRegion="new-actor-button-no-reset"/>
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (New Actor button)" path="adev/src/content/examples/forms/src/app/actor-form/actor-form.component.html" visibleRegion="new-actor-button-no-reset"/>
|
||||
|
||||
1. Run the application again and click the **New Actor** button.
|
||||
|
||||
|
|
@ -311,7 +311,7 @@ To let form users add a new actor, you will add a **New Actor** button that resp
|
|||
|
||||
1. To restore the pristine state of the form controls, clear all of the flags imperatively by calling the form's `reset()` method after calling the `newActor()` method.
|
||||
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (Reset the form)" path="forms/src/app/actor-form/actor-form.component.html" visibleRegion="new-actor-button-form-reset"/>
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (Reset the form)" path="adev/src/content/examples/forms/src/app/actor-form/actor-form.component.html" visibleRegion="new-actor-button-form-reset"/>
|
||||
|
||||
Now clicking **New Actor** resets both the form and its control flags.
|
||||
|
||||
|
|
@ -327,7 +327,7 @@ To respond to this event, take the following steps.
|
|||
<docs-step title="Listen to ngOnSubmit">
|
||||
Bind the form's [`ngSubmit`](api/forms/NgForm#properties) event property to the actor-form component's `onSubmit()` method.
|
||||
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (ngSubmit)" path="forms/src/app/actor-form/actor-form.component.html" visibleRegion="ngSubmit"/>
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (ngSubmit)" path="adev/src/content/examples/forms/src/app/actor-form/actor-form.component.html" visibleRegion="ngSubmit"/>
|
||||
</docs-step>
|
||||
|
||||
<docs-step title="Bind the disabled property">
|
||||
|
|
@ -335,7 +335,7 @@ Use the template reference variable, `#actorForm` to access the form that contai
|
|||
|
||||
You will bind the form property that indicates its overall validity to the **Submit** button's `disabled` property.
|
||||
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (submit-button)" path="forms/src/app/actor-form/actor-form.component.html" visibleRegion="submit-button"/>
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (submit-button)" path="adev/src/content/examples/forms/src/app/actor-form/actor-form.component.html" visibleRegion="submit-button"/>
|
||||
</docs-step>
|
||||
|
||||
<docs-step title="Run the application">
|
||||
|
|
@ -358,11 +358,11 @@ To show a response to form submission, you can hide the data entry area and disp
|
|||
<docs-step title="Wrap the form">
|
||||
Wrap the entire form in a `<div>` and bind its `hidden` property to the `ActorFormComponent.submitted` property.
|
||||
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (excerpt)" path="forms/src/app/actor-form/actor-form.component.html" visibleRegion="edit-div"/>
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (excerpt)" path="adev/src/content/examples/forms/src/app/actor-form/actor-form.component.html" visibleRegion="edit-div"/>
|
||||
|
||||
The main form is visible from the start because the `submitted` property is false until you submit the form, as this fragment from the `ActorFormComponent` shows:
|
||||
|
||||
<docs-code header="src/app/actor-form/actor-form.component.ts (submitted)" path="forms/src/app/actor-form/actor-form.component.ts" visibleRegion="submitted"/>
|
||||
<docs-code header="src/app/actor-form/actor-form.component.ts (submitted)" path="adev/src/content/examples/forms/src/app/actor-form/actor-form.component.ts" visibleRegion="submitted"/>
|
||||
|
||||
When you click the **Submit** button, the `submitted` flag becomes true and the form disappears.
|
||||
</docs-step>
|
||||
|
|
@ -370,7 +370,7 @@ When you click the **Submit** button, the `submitted` flag becomes true and the
|
|||
<docs-step title="Add the submitted state">
|
||||
To show something else while the form is in the submitted state, add the following HTML below the new `<div>` wrapper.
|
||||
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (excerpt)" path="forms/src/app/actor-form/actor-form.component.html" visibleRegion="submitted"/>
|
||||
<docs-code header="src/app/actor-form/actor-form.component.html (excerpt)" path="adev/src/content/examples/forms/src/app/actor-form/actor-form.component.html" visibleRegion="submitted"/>
|
||||
|
||||
This `<div>`, which shows a read-only actor with interpolation bindings, appears only while the component is in the submitted state.
|
||||
|
||||
|
|
@ -400,12 +400,12 @@ framework features to provide support for data modification, validation, and mor
|
|||
Here's the code for the final version of the application:
|
||||
|
||||
<docs-code-multifile>
|
||||
<docs-code header="actor-form/actor-form.component.ts" path="forms/src/app/actor-form/actor-form.component.ts" visibleRegion="final"/>
|
||||
<docs-code header="actor-form/actor-form.component.html" path="forms/src/app/actor-form/actor-form.component.html" visibleRegion="final"/>
|
||||
<docs-code header="actor.ts" path="forms/src/app/actor.ts"/>
|
||||
<docs-code header="app.module.ts" path="forms/src/app/app.module.ts"/>
|
||||
<docs-code header="app.component.html" path="forms/src/app/app.component.html"/>
|
||||
<docs-code header="app.component.ts" path="forms/src/app/app.component.ts"/>
|
||||
<docs-code header="main.ts" path="forms/src/main.ts"/>
|
||||
<docs-code header="forms.css" path="forms/src/assets/forms.css"/>
|
||||
<docs-code header="actor-form/actor-form.component.ts" path="adev/src/content/examples/forms/src/app/actor-form/actor-form.component.ts" visibleRegion="final"/>
|
||||
<docs-code header="actor-form/actor-form.component.html" path="adev/src/content/examples/forms/src/app/actor-form/actor-form.component.html" visibleRegion="final"/>
|
||||
<docs-code header="actor.ts" path="adev/src/content/examples/forms/src/app/actor.ts"/>
|
||||
<docs-code header="app.module.ts" path="adev/src/content/examples/forms/src/app/app.module.ts"/>
|
||||
<docs-code header="app.component.html" path="adev/src/content/examples/forms/src/app/app.component.html"/>
|
||||
<docs-code header="app.component.ts" path="adev/src/content/examples/forms/src/app/app.component.ts"/>
|
||||
<docs-code header="main.ts" path="adev/src/content/examples/forms/src/main.ts"/>
|
||||
<docs-code header="forms.css" path="adev/src/content/examples/forms/src/assets/forms.css"/>
|
||||
</docs-code-multifile>
|
||||
|
|
|
|||
Loading…
Reference in a new issue