|
| 1 | +# Building dynamic forms |
| 2 | + |
| 3 | +Many forms, such as questionaires, can be very similar to one another in format and intent. |
| 4 | +To make it faster and easier to generate different versions of such a form, |
| 5 | +you can create a *dynamic form template* based on metadata that describes the business object model. |
| 6 | +You can then use the template to generate new forms automatically, according to changes in the data model. |
| 7 | + |
| 8 | +The technique is particularly useful when you have a type of form whose content must |
| 9 | +change frequently to meet rapidly changing business and regulatory requirements. |
| 10 | +A typical use case is a questionaire. You might need to get input from users in different contexts. |
| 11 | +The format and style of the forms a user sees should remain constant, while the actual questions you need to ask vary with the context. |
| 12 | + |
| 13 | +In this tutorial you will build a dynamic form that presents a basic questionaire. |
| 14 | +You will build an online application for heroes seeking employment. |
| 15 | +The agency is constantly tinkering with the application process, but by using the dynamic form |
| 16 | +you can create the new forms on the fly without changing the application code. |
| 17 | + |
| 18 | +The tutorial walks you through the following steps. |
| 19 | + |
| 20 | +1. Enable reactive forms for a project. |
| 21 | +2. Establish a data model to represent form controls. |
| 22 | +3. Populate the model with sample data. |
| 23 | +4. Develop a component to create form controls dynamically. |
| 24 | + |
| 25 | +The form you create uses input validation and styling to improve the user experience. |
| 26 | +It has a Submit button that is only enabled when all user input is valid, and flags invalid input with color coding and error messages. |
| 27 | + |
| 28 | +The basic version can evolve to support a richer variety of questions, more graceful rendering, and superior user experience. |
| 29 | + |
| 30 | +<div class="alert is-helpful"> |
| 31 | + |
| 32 | +See the <live-example name="dynamic-form"></live-example>. |
| 33 | + |
| 34 | +</div> |
| 35 | + |
| 36 | +## Prerequisites |
| 37 | + |
| 38 | +Before doing this tutorial, you should have a basic understanding to the following. |
| 39 | + |
| 40 | +* [TypeScript](https://www.typescriptlang.org/docs/home.html "The TypeScript language") and HTML5 programming. |
| 41 | + |
| 42 | +* Fundamental concepts of [Angular app design](guide/architecture "Introduction to Angular app-design concepts"). |
| 43 | + |
| 44 | +* Basic knowledge of [reactive forms](guide/reactive-forms "Reactive forms guide"). |
| 45 | + |
| 46 | +## Enable reactive forms for your project |
| 47 | + |
| 48 | +Dynamic forms are based on reactive forms. To give the application access reactive forms directives, the [root module](guide/bootstrapping "Learn about bootstrapping an app from the root module.") imports `ReactiveFormsModule` from the `@angular/forms` library. |
| 49 | + |
| 50 | +The following code from the example shows the setup in the root module. |
| 51 | + |
| 52 | +<code-tabs> |
| 53 | + |
| 54 | + <code-pane header="app.module.ts" path="dynamic-form/src/app/app.module.ts"> |
| 55 | + |
| 56 | + </code-pane> |
| 57 | + |
| 58 | + <code-pane header="main.ts" path="dynamic-form/src/main.ts"> |
| 59 | + |
| 60 | + </code-pane> |
| 61 | + |
| 62 | +</code-tabs> |
| 63 | + |
| 64 | +{@a object-model} |
| 65 | + |
| 66 | +## Create a form object model |
| 67 | + |
| 68 | +A dynamic form requires an object model that can describe all scenarios needed by the form functionality. |
| 69 | +The example hero-application form is a set of questions—that is, each control in the form must ask a question and accept an answer. |
| 70 | + |
| 71 | +The data model for this type of form must represent a question. |
| 72 | +The example includes the `DynamicFormQuestionComponent`, which defines a question as the fundamental object in the model. |
| 73 | + |
| 74 | +The following `QuestionBase` is a base class for a set of controls that can represent the question and its answer in the form. |
| 75 | + |
| 76 | +<code-example path="dynamic-form/src/app/question-base.ts" header="src/app/question-base.ts"> |
| 77 | + |
| 78 | +</code-example> |
| 79 | + |
| 80 | +### Define control classes |
| 81 | + |
| 82 | +From this base, the example derives two new classes, `TextboxQuestion` and `DropdownQuestion`, |
| 83 | +that represent different control types. |
| 84 | +When you create the form template in the next step, you will instantiate these specific question types in order to render the appropriate controls dynamically. |
| 85 | + |
| 86 | +* The `TextboxQuestion` control type presents a question and allows users to enter input. |
| 87 | + |
| 88 | + <code-example path="dynamic-form/src/app/question-textbox.ts" header="src/app/question-textbox.ts"></code-example> |
| 89 | + |
| 90 | + The `TextboxQuestion` control type will be represented in a form template using an `<input>` element. |
| 91 | + The `type` attribute of the element will be defined based on the `type` field specified in the `options` argument (for example `text`, `email`, `url`). |
| 92 | + |
| 93 | +* The `DropdownQuestion` control presents a list of choices in a select box. |
| 94 | + |
| 95 | + <code-example path="dynamic-form/src/app/question-dropdown.ts" header="src/app/question-dropdown.ts"></code-example> |
| 96 | + |
| 97 | +### Compose form groups |
| 98 | + |
| 99 | +A dynamic form uses a service to create grouped sets of input controls, based on the form model. |
| 100 | +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. |
| 101 | + |
| 102 | +<code-example path="dynamic-form/src/app/question-control.service.ts" header="src/app/question-control.service.ts"></code-example> |
| 103 | + |
| 104 | +{@a form-component} |
| 105 | + |
| 106 | +## Compose dynamic form contents |
| 107 | + |
| 108 | +The dynamic form itself will be represented by a container component, which you will add in a later step. |
| 109 | +Each question is represented in the form component's template by an `<app-question>` tag, which matches an instance of `DynamicFormQuestionComponent`. |
| 110 | + |
| 111 | +The `DynamicFormQuestionComponent` is responsible for rendering the details of an individual question based on values in the data-bound question object. |
| 112 | +The form relies on a [`[formGroup]` directive](api/forms/FormGroupDirective "API reference") to connect the template HTML to the underlying control objects. |
| 113 | +The `DynamicFormQuestionComponent` creates form groups and populates them with controls defined in the question model, specifying display and validation rules. |
| 114 | + |
| 115 | +<code-tabs> |
| 116 | + |
| 117 | + <code-pane header="dynamic-form-question.component.html" path="dynamic-form/src/app/dynamic-form-question.component.html"> |
| 118 | + |
| 119 | + </code-pane> |
| 120 | + |
| 121 | + <code-pane header="dynamic-form-question.component.ts" path="dynamic-form/src/app/dynamic-form-question.component.ts"> |
| 122 | + |
| 123 | + </code-pane> |
| 124 | + |
| 125 | +</code-tabs> |
| 126 | + |
| 127 | +The goal of the `DynamicFormQuestionComponent` is to present question types defined in your model. |
| 128 | +You only have two types of questions at this point but you can imagine many more. |
| 129 | +The `ngSwitch` statement in the template determines which type of question to display. |
| 130 | +The switch uses directives with the [`formControlName`](api/forms/FormControlName "FormControlName directive API reference") and [`formGroup`](api/forms/FormGroupDirective "FormGroupDirective API reference") selectors. Both directives are defined in `ReactiveFormsModule`. |
| 131 | + |
| 132 | +{@a questionnaire-data} |
| 133 | + |
| 134 | +### Supply data |
| 135 | + |
| 136 | +Another service is needed to supply a specific set of questions from which to build an individual form. |
| 137 | +For this exercise you will create the `QuestionService` to supply this array of questions from the hard-coded sample data. |
| 138 | +In a real-world app, the service might fetch data from a backend system. |
| 139 | +The key point, however, is that you control the hero job-application questions entirely through the objects returned from `QuestionService`. |
| 140 | +To maintain the questionnaire as requirements change, you only need to add, update, and remove objects from the `questions` array. |
| 141 | + |
| 142 | + |
| 143 | +The `QuestionService` supplies a set of questions in the form of an array bound to `@Input()` questions. |
| 144 | + |
| 145 | +<code-example path="dynamic-form/src/app/question.service.ts" header="src/app/question.service.ts"> |
| 146 | + |
| 147 | +</code-example> |
| 148 | + |
| 149 | + |
| 150 | +{@a dynamic-template} |
| 151 | + |
| 152 | +## Create a dynamic form template |
| 153 | + |
| 154 | +The `DynamicFormComponent` component is the entry point and the main container for the form, which is represented using the `<app-dynamic-form>` in a template. |
| 155 | + |
| 156 | +The `DynamicFormComponent` component presents a list of questions by binding each one to an `<app-question>` element that matches the `DynamicFormQuestionComponent`. |
| 157 | + |
| 158 | +<code-tabs> |
| 159 | + |
| 160 | + <code-pane header="dynamic-form.component.html" path="dynamic-form/src/app/dynamic-form.component.html"> |
| 161 | + |
| 162 | + </code-pane> |
| 163 | + |
| 164 | + <code-pane header="dynamic-form.component.ts" path="dynamic-form/src/app/dynamic-form.component.ts"> |
| 165 | + |
| 166 | + </code-pane> |
| 167 | + |
| 168 | +</code-tabs> |
| 169 | + |
| 170 | +### Display the form |
| 171 | + |
| 172 | +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>`. |
| 173 | + |
| 174 | +<code-example path="dynamic-form/src/app/app.component.ts" header="app.component.ts"> |
| 175 | + |
| 176 | +</code-example> |
| 177 | + |
| 178 | +The example provides a model for a job application for heroes, but there are |
| 179 | +no references to any specific hero question other than the objects returned by `QuestionService`. |
| 180 | +This separation of model and data allows you to repurpose the components for any type of survey |
| 181 | +as long as it's compatible with the *question* object model. |
| 182 | + |
| 183 | +### Ensuring valid data |
| 184 | + |
| 185 | +The form template uses dynamic data binding of metadata to render the form |
| 186 | +without making any hardcoded assumptions about specific questions. |
| 187 | +It adds both control metadata and validation criteria dynamically. |
| 188 | + |
| 189 | +To ensure valid input, the *Save* button is disabled until the form is in a valid state. |
| 190 | +When the form is valid, you can click *Save* and the app renders the current form values as JSON. |
| 191 | + |
| 192 | +The following figure shows the final form. |
| 193 | + |
| 194 | +<div class="lightbox"> |
| 195 | + <img src="generated/images/guide/dynamic-form/dynamic-form.png" alt="Dynamic-Form"> |
| 196 | +</div> |
| 197 | + |
| 198 | +## Next steps |
| 199 | + |
| 200 | +* **Different types of forms and control collection** |
| 201 | + |
| 202 | + This tutorial shows how to build a a questionaire, which is just one kind of dynamic form. |
| 203 | + The example uses `FormGroup` to collect a set of controls. |
| 204 | + For an example of a different type of dynamic form, see the section [Creating dynamic forms](guide/reactive-forms#creating-dynamic-forms "Create dynamic forms with arrays") in the Reactive Forms guide. |
| 205 | + That example also shows how to use `FormArray` instead of `FormGroup` to collect a set of controls. |
| 206 | + |
| 207 | +* **Validating user input** |
| 208 | + |
| 209 | + The section [Validating form input](guide/reactive-forms#validating-form-input "Basic input validation") introduces the basics of how input validation works in reactive forms. |
| 210 | + |
| 211 | + The [Form validation guide](guide/form-validation "Form validation guide") covers the topic in more depth. |
0 commit comments