angular-cn/aio/dist/generated/docs/guide/dynamic-form.json

5 lines
26 KiB
JSON

{
"id": "guide/dynamic-form",
"title": "Building dynamic forms",
"contents": "\n\n\n<div class=\"github-links\">\n <a href=\"https://github.com/angular/angular/edit/master/aio/content/guide/dynamic-form.md?message=docs%3A%20describe%20your%20change...\" aria-label=\"Suggest Edits\" title=\"Suggest Edits\"><i class=\"material-icons\" aria-hidden=\"true\" role=\"img\">mode_edit</i></a>\n</div>\n\n\n<div class=\"content\">\n <h1 id=\"building-dynamic-forms\">Building dynamic forms<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/dynamic-form#building-dynamic-forms\"><i class=\"material-icons\">link</i></a></h1>\n<p>Many forms, such as questionaires, can be very similar to one another in format and intent.\nTo make it faster and easier to generate different versions of such a form,\nyou can create a <em>dynamic form template</em> based on metadata that describes the business object model.\nYou can then use the template to generate new forms automatically, according to changes in the data model.</p>\n<p>The technique is particularly useful when you have a type of form whose content must\nchange frequently to meet rapidly changing business and regulatory requirements.\nA typical use case is a questionaire. You might need to get input from users in different contexts.\nThe format and style of the forms a user sees should remain constant, while the actual questions you need to ask vary with the context.</p>\n<p>In this tutorial you will build a dynamic form that presents a basic questionaire.\nYou will build an online application for heroes seeking employment.\nThe agency is constantly tinkering with the application process, but by using the dynamic form\nyou can create the new forms on the fly without changing the application code.</p>\n<p>The tutorial walks you through the following steps.</p>\n<ol>\n<li>Enable reactive forms for a project.</li>\n<li>Establish a data model to represent form controls.</li>\n<li>Populate the model with sample data.</li>\n<li>Develop a component to create form controls dynamically.</li>\n</ol>\n<p>The form you create uses input validation and styling to improve the user experience.\nIt has a Submit button that is only enabled when all user input is valid, and flags invalid input with color coding and error messages.</p>\n<p>The basic version can evolve to support a richer variety of questions, more graceful rendering, and superior user experience.</p>\n<div class=\"alert is-helpful\">\n<p>See the <live-example name=\"dynamic-form\"></live-example>.</p>\n</div>\n<h2 id=\"prerequisites\">Prerequisites<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/dynamic-form#prerequisites\"><i class=\"material-icons\">link</i></a></h2>\n<p>Before doing this tutorial, you should have a basic understanding to the following.</p>\n<ul>\n<li>\n<p><a href=\"https://www.typescriptlang.org/\" title=\"The TypeScript language\">TypeScript</a> and HTML5 programming.</p>\n</li>\n<li>\n<p>Fundamental concepts of <a href=\"guide/architecture\" title=\"Introduction to Angular app-design concepts\">Angular app design</a>.</p>\n</li>\n<li>\n<p>Basic knowledge of <a href=\"guide/reactive-forms\" title=\"Reactive forms guide\">reactive forms</a>.</p>\n</li>\n</ul>\n<h2 id=\"enable-reactive-forms-for-your-project\">Enable reactive forms for your project<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/dynamic-form#enable-reactive-forms-for-your-project\"><i class=\"material-icons\">link</i></a></h2>\n<p>Dynamic forms are based on reactive forms. To give the application access reactive forms directives, the <a href=\"guide/bootstrapping\" title=\"Learn about bootstrapping an app from the root module.\">root module</a> imports <code><a href=\"api/forms/ReactiveFormsModule\" class=\"code-anchor\">ReactiveFormsModule</a></code> from the <code>@angular/forms</code> library.</p>\n<p>The following code from the example shows the setup in the root module.</p>\n<code-tabs>\n\n <code-pane header=\"app.module.ts\" path=\"dynamic-form/src/app/app.module.ts\">\nimport { <a href=\"api/platform-browser/BrowserModule\" class=\"code-anchor\">BrowserModule</a> } from '@angular/platform-browser';\nimport { <a href=\"api/forms/ReactiveFormsModule\" class=\"code-anchor\">ReactiveFormsModule</a> } from '@angular/forms';\nimport { <a href=\"api/core/NgModule\" class=\"code-anchor\">NgModule</a> } from '@angular/core';\n\nimport { AppComponent } from './app.component';\nimport { DynamicFormComponent } from './dynamic-form.component';\nimport { DynamicFormQuestionComponent } from './dynamic-form-question.component';\n\n@<a href=\"api/core/NgModule\" class=\"code-anchor\">NgModule</a>({\n imports: [ <a href=\"api/platform-browser/BrowserModule\" class=\"code-anchor\">BrowserModule</a>, <a href=\"api/forms/ReactiveFormsModule\" class=\"code-anchor\">ReactiveFormsModule</a> ],\n declarations: [ AppComponent, DynamicFormComponent, DynamicFormQuestionComponent ],\n bootstrap: [ AppComponent ]\n})\nexport class AppModule {\n constructor() {\n }\n}\n\n\n</code-pane>\n\n <code-pane header=\"main.ts\" path=\"dynamic-form/src/main.ts\">\nimport { <a href=\"api/core/enableProdMode\" class=\"code-anchor\">enableProdMode</a> } from '@angular/core';\nimport { <a href=\"api/platform-browser-dynamic/platformBrowserDynamic\" class=\"code-anchor\">platformBrowserDynamic</a> } from '@angular/platform-browser-dynamic';\n\nimport { AppModule } from './app/app.module';\nimport { environment } from './environments/environment';\n\nif (environment.production) {\n <a href=\"api/core/enableProdMode\" class=\"code-anchor\">enableProdMode</a>();\n}\n\n<a href=\"api/platform-browser-dynamic/platformBrowserDynamic\" class=\"code-anchor\">platformBrowserDynamic</a>().bootstrapModule(AppModule);\n\n\n</code-pane>\n\n</code-tabs>\n<a id=\"object-model\"></a>\n<h2 id=\"create-a-form-object-model\">Create a form object model<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/dynamic-form#create-a-form-object-model\"><i class=\"material-icons\">link</i></a></h2>\n<p>A dynamic form requires an object model that can describe all scenarios needed by the form functionality.\nThe example hero-application form is a set of questions—that is, each control in the form must ask a question and accept an answer.</p>\n<p>The data model for this type of form must represent a question.\nThe example includes the <code>DynamicFormQuestionComponent</code>, which defines a question as the fundamental object in the model.</p>\n<p>The following <code>QuestionBase</code> is a base class for a set of controls that can represent the question and its answer in the form.</p>\n<code-example path=\"dynamic-form/src/app/question-base.ts\" header=\"src/app/question-base.ts\">\nexport class QuestionBase&#x3C;T> {\n value: T;\n key: string;\n label: string;\n required: boolean;\n order: number;\n controlType: string;\n type: string;\n options: {key: string, value: string}[];\n\n constructor(options: {\n value?: T;\n key?: string;\n label?: string;\n required?: boolean;\n order?: number;\n controlType?: string;\n type?: string;\n options?: {key: string, value: string}[];\n } = {}) {\n this.value = options.value;\n this.key = options.key || '';\n this.label = options.label || '';\n this.required = !!options.required;\n this.order = options.order === undefined ? 1 : options.order;\n this.controlType = options.controlType || '';\n this.type = options.type || '';\n this.options = options.options || [];\n }\n}\n\n\n</code-example>\n<h3 id=\"define-control-classes\">Define control classes<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/dynamic-form#define-control-classes\"><i class=\"material-icons\">link</i></a></h3>\n<p>From this base, the example derives two new classes, <code>TextboxQuestion</code> and <code>DropdownQuestion</code>,\nthat represent different control types.\nWhen you create the form template in the next step, you will instantiate these specific question types in order to render the appropriate controls dynamically.</p>\n<ul>\n<li>\n<p>The <code>TextboxQuestion</code> control type presents a question and allows users to enter input.</p>\n<p> <code-example path=\"dynamic-form/src/app/question-textbox.ts\" header=\"src/app/question-textbox.ts\">\nimport { QuestionBase } from './question-base';\n\nexport class TextboxQuestion extends QuestionBase&#x3C;string> {\n controlType = 'textbox';\n}\n\n\n</code-example></p>\n<p> The <code>TextboxQuestion</code> control type will be represented in a form template using an <code>&#x3C;input></code> element.\nThe <code>type</code> attribute of the element will be defined based on the <code>type</code> field specified in the <code>options</code> argument (for example <code>text</code>, <code><a href=\"api/forms/EmailValidator\" class=\"code-anchor\">email</a></code>, <code>url</code>).</p>\n</li>\n<li>\n<p>The <code>DropdownQuestion</code> control presents a list of choices in a select box.</p>\n<p> <code-example path=\"dynamic-form/src/app/question-dropdown.ts\" header=\"src/app/question-dropdown.ts\">\nimport { QuestionBase } from './question-base';\n\nexport class DropdownQuestion extends QuestionBase&#x3C;string> {\n controlType = 'dropdown';\n}\n\n\n</code-example></p>\n</li>\n</ul>\n<h3 id=\"compose-form-groups\">Compose form groups<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/dynamic-form#compose-form-groups\"><i class=\"material-icons\">link</i></a></h3>\n<p>A dynamic form uses a service to create grouped sets of input controls, based on the form model.\nThe following <code>QuestionControlService</code> collects a set of <code><a href=\"api/forms/FormGroup\" class=\"code-anchor\">FormGroup</a></code> instances that consume the metadata from the question model. You can specify default values and validation rules.</p>\n<code-example path=\"dynamic-form/src/app/question-control.service.ts\" header=\"src/app/question-control.service.ts\">\nimport { <a href=\"api/core/Injectable\" class=\"code-anchor\">Injectable</a> } from '@angular/core';\nimport { <a href=\"api/forms/FormControl\" class=\"code-anchor\">FormControl</a>, <a href=\"api/forms/FormGroup\" class=\"code-anchor\">FormGroup</a>, <a href=\"api/forms/Validators\" class=\"code-anchor\">Validators</a> } from '@angular/forms';\n\nimport { QuestionBase } from './question-base';\n\n@<a href=\"api/core/Injectable\" class=\"code-anchor\">Injectable</a>()\nexport class QuestionControlService {\n constructor() { }\n\n toFormGroup(questions: QuestionBase&#x3C;string>[] ) {\n const group: any = {};\n\n questions.forEach(question => {\n group[question.key] = question.required ? new <a href=\"api/forms/FormControl\" class=\"code-anchor\">FormControl</a>(question.value || '', Validators.required)\n : new <a href=\"api/forms/FormControl\" class=\"code-anchor\">FormControl</a>(question.value || '');\n });\n return new <a href=\"api/forms/FormGroup\" class=\"code-anchor\">FormGroup</a>(group);\n }\n}\n\n\n</code-example>\n<a id=\"form-component\"></a>\n<h2 id=\"compose-dynamic-form-contents\">Compose dynamic form contents<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/dynamic-form#compose-dynamic-form-contents\"><i class=\"material-icons\">link</i></a></h2>\n<p>The dynamic form itself will be represented by a container component, which you will add in a later step.\nEach question is represented in the form component's template by an <code>&#x3C;app-question></code> tag, which matches an instance of <code>DynamicFormQuestionComponent</code>.</p>\n<p>The <code>DynamicFormQuestionComponent</code> is responsible for rendering the details of an individual question based on values in the data-bound question object.\nThe form relies on a <a href=\"api/forms/FormGroupDirective\" title=\"API reference\"><code>[formGroup]</code> directive</a> to connect the template HTML to the underlying control objects.\nThe <code>DynamicFormQuestionComponent</code> creates form groups and populates them with controls defined in the question model, specifying display and validation rules.</p>\n<code-tabs>\n\n <code-pane header=\"dynamic-form-question.component.html\" path=\"dynamic-form/src/app/dynamic-form-question.component.html\">\n&#x3C;div [formGroup]=\"form\">\n &#x3C;label [attr.for]=\"question.key\">{{question.label}}&#x3C;/label>\n\n &#x3C;div [<a href=\"api/common/NgSwitch\" class=\"code-anchor\">ngSwitch</a>]=\"question.controlType\">\n\n &#x3C;input *<a href=\"api/common/NgSwitchCase\" class=\"code-anchor\">ngSwitchCase</a>=\"'textbox'\" [<a href=\"api/forms/FormControlName\" class=\"code-anchor\">formControlName</a>]=\"question.key\"\n [id]=\"question.key\" [type]=\"question.type\">\n\n &#x3C;select [id]=\"question.key\" *<a href=\"api/common/NgSwitchCase\" class=\"code-anchor\">ngSwitchCase</a>=\"'dropdown'\" [<a href=\"api/forms/FormControlName\" class=\"code-anchor\">formControlName</a>]=\"question.key\">\n &#x3C;option *<a href=\"api/common/NgForOf\" class=\"code-anchor\">ngFor</a>=\"let opt of question.options\" [value]=\"opt.key\">{{opt.value}}&#x3C;/option>\n &#x3C;/select>\n\n &#x3C;/div>\n\n &#x3C;div class=\"errorMessage\" *<a href=\"api/common/NgIf\" class=\"code-anchor\">ngIf</a>=\"!isValid\">{{question.label}} is required&#x3C;/div>\n&#x3C;/div>\n\n\n</code-pane>\n\n <code-pane header=\"dynamic-form-question.component.ts\" path=\"dynamic-form/src/app/dynamic-form-question.component.ts\">\nimport { <a href=\"api/core/Component\" class=\"code-anchor\">Component</a>, <a href=\"api/core/Input\" class=\"code-anchor\">Input</a> } from '@angular/core';\nimport { <a href=\"api/forms/FormGroup\" class=\"code-anchor\">FormGroup</a> } from '@angular/forms';\n\nimport { QuestionBase } from './question-base';\n\n@<a href=\"api/core/Component\" class=\"code-anchor\">Component</a>({\n selector: 'app-question',\n templateUrl: './dynamic-form-question.component.html'\n})\nexport class DynamicFormQuestionComponent {\n @<a href=\"api/core/Input\" class=\"code-anchor\">Input</a>() question: QuestionBase&#x3C;string>;\n @<a href=\"api/core/Input\" class=\"code-anchor\">Input</a>() form: <a href=\"api/forms/FormGroup\" class=\"code-anchor\">FormGroup</a>;\n get isValid() { return this.form.controls[this.question.key].valid; }\n}\n\n\n</code-pane>\n\n</code-tabs>\n<p>The goal of the <code>DynamicFormQuestionComponent</code> is to present question types defined in your model.\nYou only have two types of questions at this point but you can imagine many more.\nThe <code><a href=\"api/common/NgSwitch\" class=\"code-anchor\">ngSwitch</a></code> statement in the template determines which type of question to display.\nThe switch uses directives with the <a href=\"api/forms/FormControlName\" title=\"FormControlName directive API reference\"><code>formControlName</code></a> and <a href=\"api/forms/FormGroupDirective\" title=\"FormGroupDirective API reference\"><code>formGroup</code></a> selectors. Both directives are defined in <code><a href=\"api/forms/ReactiveFormsModule\" class=\"code-anchor\">ReactiveFormsModule</a></code>.</p>\n<a id=\"questionnaire-data\"></a>\n<h3 id=\"supply-data\">Supply data<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/dynamic-form#supply-data\"><i class=\"material-icons\">link</i></a></h3>\n<p>Another service is needed to supply a specific set of questions from which to build an individual form.\nFor this exercise you will create the <code>QuestionService</code> to supply this array of questions from the hard-coded sample data.\nIn a real-world app, the service might fetch data from a backend system.\nThe key point, however, is that you control the hero job-application questions entirely through the objects returned from <code>QuestionService</code>.\nTo maintain the questionnaire as requirements change, you only need to add, update, and remove objects from the <code>questions</code> array.</p>\n<p>The <code>QuestionService</code> supplies a set of questions in the form of an array bound to <code>@<a href=\"api/core/Input\" class=\"code-anchor\">Input</a>()</code> questions.</p>\n<code-example path=\"dynamic-form/src/app/question.service.ts\" header=\"src/app/question.service.ts\">\nimport { <a href=\"api/core/Injectable\" class=\"code-anchor\">Injectable</a> } from '@angular/core';\n\nimport { DropdownQuestion } from './question-dropdown';\nimport { QuestionBase } from './question-base';\nimport { TextboxQuestion } from './question-textbox';\nimport { of } from 'rxjs';\n\n@<a href=\"api/core/Injectable\" class=\"code-anchor\">Injectable</a>()\nexport class QuestionService {\n\n // TODO: get from a remote source of question metadata\n getQuestions() {\n\n const questions: QuestionBase&#x3C;string>[] = [\n\n new DropdownQuestion({\n key: 'brave',\n label: 'Bravery Rating',\n options: [\n {key: 'solid', value: 'Solid'},\n {key: 'great', value: 'Great'},\n {key: 'good', value: 'Good'},\n {key: 'unproven', value: 'Unproven'}\n ],\n order: 3\n }),\n\n new TextboxQuestion({\n key: 'firstName',\n label: 'First name',\n value: 'Bombasto',\n required: true,\n order: 1\n }),\n\n new TextboxQuestion({\n key: 'emailAddress',\n label: 'Email',\n type: '<a href=\"api/forms/EmailValidator\" class=\"code-anchor\">email</a>',\n order: 2\n })\n ];\n\n return of(questions.sort((a, b) => a.order - b.order));\n }\n}\n\n\n</code-example>\n<a id=\"dynamic-template\"></a>\n<h2 id=\"create-a-dynamic-form-template\">Create a dynamic form template<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/dynamic-form#create-a-dynamic-form-template\"><i class=\"material-icons\">link</i></a></h2>\n<p>The <code>DynamicFormComponent</code> component is the entry point and the main container for the form, which is represented using the <code>&#x3C;app-dynamic-form></code> in a template.</p>\n<p>The <code>DynamicFormComponent</code> component presents a list of questions by binding each one to an <code>&#x3C;app-question></code> element that matches the <code>DynamicFormQuestionComponent</code>.</p>\n<code-tabs>\n\n <code-pane header=\"dynamic-form.component.html\" path=\"dynamic-form/src/app/dynamic-form.component.html\">\n&#x3C;div>\n &#x3C;form (ngSubmit)=\"onSubmit()\" [formGroup]=\"form\">\n\n &#x3C;div *<a href=\"api/common/NgForOf\" class=\"code-anchor\">ngFor</a>=\"let question of questions\" class=\"form-row\">\n &#x3C;app-question [question]=\"question\" [form]=\"form\">&#x3C;/app-question>\n &#x3C;/div>\n\n &#x3C;div class=\"form-row\">\n &#x3C;button type=\"submit\" [disabled]=\"!form.valid\">Save&#x3C;/button>\n &#x3C;/div>\n &#x3C;/form>\n\n &#x3C;div *<a href=\"api/common/NgIf\" class=\"code-anchor\">ngIf</a>=\"payLoad\" class=\"form-row\">\n &#x3C;strong>Saved the following values&#x3C;/strong>&#x3C;br>{{payLoad}}\n &#x3C;/div>\n&#x3C;/div>\n\n\n</code-pane>\n\n <code-pane header=\"dynamic-form.component.ts\" path=\"dynamic-form/src/app/dynamic-form.component.ts\">\nimport { <a href=\"api/core/Component\" class=\"code-anchor\">Component</a>, <a href=\"api/core/Input\" class=\"code-anchor\">Input</a>, <a href=\"api/core/OnInit\" class=\"code-anchor\">OnInit</a> } from '@angular/core';\nimport { <a href=\"api/forms/FormGroup\" class=\"code-anchor\">FormGroup</a> } from '@angular/forms';\n\nimport { QuestionBase } from './question-base';\nimport { QuestionControlService } from './question-control.service';\n\n@<a href=\"api/core/Component\" class=\"code-anchor\">Component</a>({\n selector: 'app-dynamic-form',\n templateUrl: './dynamic-form.component.html',\n providers: [ QuestionControlService ]\n})\nexport class DynamicFormComponent implements <a href=\"api/core/OnInit\" class=\"code-anchor\">OnInit</a> {\n\n @<a href=\"api/core/Input\" class=\"code-anchor\">Input</a>() questions: QuestionBase&#x3C;string>[] = [];\n form: <a href=\"api/forms/FormGroup\" class=\"code-anchor\">FormGroup</a>;\n payLoad = '';\n\n constructor(private qcs: QuestionControlService) { }\n\n ngOnInit() {\n this.form = this.qcs.toFormGroup(this.questions);\n }\n\n onSubmit() {\n this.payLoad = JSON.stringify(this.form.getRawValue());\n }\n}\n\n\n</code-pane>\n\n</code-tabs>\n<h3 id=\"display-the-form\">Display the form<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/dynamic-form#display-the-form\"><i class=\"material-icons\">link</i></a></h3>\n<p>To display an instance of the dynamic form, the <code>AppComponent</code> shell template passes the <code>questions</code> array returned by the <code>QuestionService</code> to the form container component, <code>&#x3C;app-dynamic-form></code>.</p>\n<code-example path=\"dynamic-form/src/app/app.component.ts\" header=\"app.component.ts\">\nimport { <a href=\"api/core/Component\" class=\"code-anchor\">Component</a> } from '@angular/core';\n\nimport { QuestionService } from './question.service';\nimport { QuestionBase } from './question-base';\nimport { Observable } from 'rxjs';\n\n@<a href=\"api/core/Component\" class=\"code-anchor\">Component</a>({\n selector: 'app-root',\n template: `\n &#x3C;div>\n &#x3C;h2>Job Application for Heroes&#x3C;/h2>\n &#x3C;app-dynamic-form [questions]=\"questions$ | async\">&#x3C;/app-dynamic-form>\n &#x3C;/div>\n `,\n providers: [QuestionService]\n})\nexport class AppComponent {\n questions$: Observable&#x3C;QuestionBase&#x3C;any>[]>;\n\n constructor(service: QuestionService) {\n this.questions$ = service.getQuestions();\n }\n}\n\n\n</code-example>\n<p>The example provides a model for a job application for heroes, but there are\nno references to any specific hero question other than the objects returned by <code>QuestionService</code>.\nThis separation of model and data allows you to repurpose the components for any type of survey\nas long as it's compatible with the <em>question</em> object model.</p>\n<h3 id=\"ensuring-valid-data\">Ensuring valid data<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/dynamic-form#ensuring-valid-data\"><i class=\"material-icons\">link</i></a></h3>\n<p>The form template uses dynamic data binding of metadata to render the form\nwithout making any hardcoded assumptions about specific questions.\nIt adds both control metadata and validation criteria dynamically.</p>\n<p>To ensure valid input, the <em>Save</em> button is disabled until the form is in a valid state.\nWhen the form is valid, you can click <em>Save</em> and the app renders the current form values as JSON.</p>\n<p>The following figure shows the final form.</p>\n<div class=\"lightbox\">\n <img src=\"generated/images/guide/dynamic-form/dynamic-form.png\" alt=\"Dynamic-Form\" width=\"316\" height=\"230\">\n</div>\n<h2 id=\"next-steps\">Next steps<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/dynamic-form#next-steps\"><i class=\"material-icons\">link</i></a></h2>\n<ul>\n<li>\n<p><strong>Different types of forms and control collection</strong></p>\n<p> This tutorial shows how to build a a questionaire, which is just one kind of dynamic form.\nThe example uses <code><a href=\"api/forms/FormGroup\" class=\"code-anchor\">FormGroup</a></code> to collect a set of controls.\nFor an example of a different type of dynamic form, see the section <a href=\"guide/reactive-forms#creating-dynamic-forms\" title=\"Create dynamic forms with arrays\">Creating dynamic forms</a> in the Reactive Forms guide.\nThat example also shows how to use <code><a href=\"api/forms/FormArray\" class=\"code-anchor\">FormArray</a></code> instead of <code><a href=\"api/forms/FormGroup\" class=\"code-anchor\">FormGroup</a></code> to collect a set of controls.</p>\n</li>\n<li>\n<p><strong>Validating user input</strong></p>\n<p> The section <a href=\"guide/reactive-forms#validating-form-input\" title=\"Basic input validation\">Validating form input</a> introduces the basics of how input validation works in reactive forms.</p>\n<p> The <a href=\"guide/form-validation\" title=\"Form validation guide\">Form validation guide</a> covers the topic in more depth.</p>\n</li>\n</ul>\n\n \n</div>\n\n<!-- links to this doc:\n - guide/example-apps-list\n - guide/forms-overview\n - guide/security\n-->\n<!-- links from this doc:\n - api/common/NgForOf\n - api/common/NgIf\n - api/common/NgSwitch\n - api/common/NgSwitchCase\n - api/core/Component\n - api/core/Injectable\n - api/core/Input\n - api/core/NgModule\n - api/core/OnInit\n - api/core/enableProdMode\n - api/forms/EmailValidator\n - api/forms/FormArray\n - api/forms/FormControl\n - api/forms/FormControlName\n - api/forms/FormGroup\n - api/forms/FormGroupDirective\n - api/forms/ReactiveFormsModule\n - api/forms/Validators\n - api/platform-browser-dynamic/platformBrowserDynamic\n - api/platform-browser/BrowserModule\n - guide/architecture\n - guide/bootstrapping\n - guide/dynamic-form#building-dynamic-forms\n - guide/dynamic-form#compose-dynamic-form-contents\n - guide/dynamic-form#compose-form-groups\n - guide/dynamic-form#create-a-dynamic-form-template\n - guide/dynamic-form#create-a-form-object-model\n - guide/dynamic-form#define-control-classes\n - guide/dynamic-form#display-the-form\n - guide/dynamic-form#enable-reactive-forms-for-your-project\n - guide/dynamic-form#ensuring-valid-data\n - guide/dynamic-form#next-steps\n - guide/dynamic-form#prerequisites\n - guide/dynamic-form#supply-data\n - guide/form-validation\n - guide/reactive-forms\n - guide/reactive-forms#creating-dynamic-forms\n - guide/reactive-forms#validating-form-input\n - https://github.com/angular/angular/edit/master/aio/content/guide/dynamic-form.md?message=docs%3A%20describe%20your%20change...\n - https://www.typescriptlang.org/\n-->"
}