From 1e208e8c87e9a558fc582cc7e43b48919223cdfd Mon Sep 17 00:00:00 2001 From: Judy Bogart Date: Mon, 6 Apr 2020 14:59:46 -0700 Subject: [PATCH] docs: refactor dynamic forms topic as tutorial (#36465) rework content to meet current documentation standards and conventions, structure as tutorial document PR Close #36465 --- aio/content/guide/dynamic-form.md | 224 +++++++++++++++--------------- aio/content/navigation.json | 4 +- 2 files changed, 117 insertions(+), 111 deletions(-) diff --git a/aio/content/guide/dynamic-form.md b/aio/content/guide/dynamic-form.md index 1e1b35ec89..a1ec9b3a64 100644 --- a/aio/content/guide/dynamic-form.md +++ b/aio/content/guide/dynamic-form.md @@ -1,42 +1,53 @@ -# Dynamic forms +# Building dynamic forms -{@a top} +Many forms, such as questionaires, can be very similar to one another in format and intent. +To make it faster and easier to generate different versions of such a form, +you can create a *dynamic form template* based on metadata that describes the business object model. +You can then use the template to generate new forms automatically, according to changes in the data model. -Building handcrafted forms can be costly and time-consuming, -especially if you need a great number of them, they're similar to each other, and they change frequently -to meet rapidly changing business and regulatory requirements. +The technique is particularly useful when you have a type of form whose content must +change frequently to meet rapidly changing business and regulatory requirements. +A typical use case is a questionaire. You might need to get input from users in different contexts. +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. -It may be more economical to create the forms dynamically, based on -metadata that describes the business object model. +In this tutorial you will build a dynamic form that presents a basic questionaire. +You will build an online application for heroes seeking employment. +The agency is constantly tinkering with the application process, but by using the dynamic form +you can create the new forms on the fly without changing the application code. -This cookbook shows you how to use `formGroup` to dynamically -render a simple form with different control types and validation. -It's a primitive start. -It might evolve to support a much richer variety of questions, more graceful rendering, and superior user experience. -All such greatness has humble beginnings. +The tutorial walks you through the following steps. -The example in this cookbook is a dynamic form to build an -online application experience for heroes seeking employment. -The agency is constantly tinkering with the application process. -You can create the forms on the fly *without changing the application code*. -{@a toc} +1. Enable reactive forms for a project. +2. Establish a data model to represent form controls. +3. Populate the model with sample data. +4. Develop a component to create form controls dynamically. + +The form you create uses input validation and styling to improve the user experience. +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. + +The basic version can evolve to support a richer variety of questions, more graceful rendering, and superior user experience. + +
See the . -{@a bootstrap} +
-## Bootstrap +## Prerequisites -Start by creating an `NgModule` called `AppModule`. +Before doing this tutorial, you should have a basic understanding to the following. -This cookbook uses [reactive forms](guide/reactive-forms). +* [TypeScript](https://www.typescriptlang.org/docs/home.html "The TypeScript language") and HTML5 programming. -Reactive forms belongs to a different `NgModule` called `ReactiveFormsModule`, -so in order to access any reactive forms directives, you have to import -`ReactiveFormsModule` from the `@angular/forms` library. +* Fundamental concepts of [Angular app design](guide/architecture "Introduction to Angular app-design concepts"). -Bootstrap the `AppModule` in `main.ts`. +* Basic knowledge of [reactive forms](guide/reactive-forms "Reactive forms guide"). +## Enable reactive forms for your project + +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. + +The following code from the example shows the setup in the root module. @@ -50,79 +61,56 @@ Bootstrap the `AppModule` in `main.ts`. - {@a object-model} -## Question model +## Create a form object model -The next step is to define an object model that can describe all scenarios needed by the form functionality. -The hero application process involves a form with a lot of questions. -The _question_ is the most fundamental object in the model. +A dynamic form requires an object model that can describe all scenarios needed by the form functionality. +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. -The following `QuestionBase` is a fundamental question class. +The data model for this type of form must represent a question. +The example includes the `DynamicFormQuestionComponent`, which defines a question as the fundamental object in the model. +The following `QuestionBase` is a base class for a set of controls that can represent the question and its answer in the form. +### Define control classes +From this base, the example derives two new classes, `TextboxQuestion` and `DropdownQuestion`, +that represent different control types. +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. -From this base you can derive two new classes in `TextboxQuestion` and `DropdownQuestion` -that represent textbox and dropdown questions. -The idea is that the form will be bound to specific question types and render the -appropriate controls dynamically. +* The `TextboxQuestion` control type presents a question and allows users to enter input. -`TextboxQuestion` supports multiple HTML5 types such as text, email, and url -via the `type` property. + + The `TextboxQuestion` control type will be represented in a form template using an `` element. + The `type` attribute of the element will be defined based on the `type` field specified in the `options` argument (for example `text`, `email`, `url`). - +* The `DropdownQuestion` control presents a list of choices in a select box. + +### Compose form groups -`DropdownQuestion` presents a list of choices in a select box. - - - - - - -Next is `QuestionControlService`, a simple service for transforming the questions to a `FormGroup`. -In a nutshell, the form group consumes the metadata from the question model and -allows you to specify default values and validation rules. - +A dynamic form uses a service to create grouped sets of input controls, based on the form model. +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. {@a form-component} -## Question form components -Now that you have defined the complete model you are ready -to create components to represent the dynamic form. +## Compose dynamic form contents +The dynamic form itself will be represented by a container component, which you will add in a later step. +Each question is represented in the form component's template by an `` tag, which matches an instance of `DynamicFormQuestionComponent`. -`DynamicFormComponent` is the entry point and the main container for the form. - - - - - - - - - - - - - - - -It presents a list of questions, each bound to a `` component element. -The `` tag matches the `DynamicFormQuestionComponent`, -the component responsible for rendering the details of each _individual_ -question based on values in the data-bound question object. - +The `DynamicFormQuestionComponent` is responsible for rendering the details of an individual question based on values in the data-bound question object. +The form relies on a [`[formGroup]` directive](api/forms/FormGroupDirective "API reference") to connect the template HTML to the underlying control objects. +The `DynamicFormQuestionComponent` creates form groups and populates them with controls defined in the question model, specifying display and validation rules. @@ -136,70 +124,88 @@ question based on values in the data-bound question object. - - -Notice this component can present any type of question in your model. +The goal of the `DynamicFormQuestionComponent` is to present question types defined in your model. You only have two types of questions at this point but you can imagine many more. -The `ngSwitch` determines which type of question to display. +The `ngSwitch` statement in the template determines which type of question to display. +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`. -In both components you're relying on Angular's **formGroup** to connect the template HTML to the -underlying control objects, populated from the question model with display and validation rules. - -`formControlName` and `formGroup` are directives defined in -`ReactiveFormsModule`. The templates can access these directives -directly since you imported `ReactiveFormsModule` from `AppModule`. {@a questionnaire-data} -## Questionnaire data +### Supply data -`DynamicFormComponent` expects the list of questions in the form of an array bound to `@Input() questions`. +Another service is needed to supply a specific set of questions from which to build an individual form. +For this exercise you will create the `QuestionService` to supply this array of questions from the hard-coded sample data. +In a real-world app, the service might fetch data from a backend system. +The key point, however, is that you control the hero job-application questions entirely through the objects returned from `QuestionService`. +To maintain the questionnaire as requirements change, you only need to add, update, and remove objects from the `questions` array. - The set of questions you've defined for the job application is returned from the `QuestionService`. - In a real app you'd retrieve these questions from storage. - - The key point is that you control the hero job application questions - entirely through the objects returned from `QuestionService`. - Questionnaire maintenance is a simple matter of adding, updating, - and removing objects from the `questions` array. +The `QuestionService` supplies a set of questions in the form of an array bound to `@Input()` questions. +{@a dynamic-template} -Finally, display an instance of the form in the `AppComponent` shell. +## Create a dynamic form template +The `DynamicFormComponent` component is the entry point and the main container for the form, which is represented using the `` in a template. + +The `DynamicFormComponent` component presents a list of questions by binding each one to an `` element that matches the `DynamicFormQuestionComponent`. + + + + + + + + + + + + + +### 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, ``. -{@a dynamic-template} - -## Dynamic Template -Although in this example you're modelling a job application for heroes, there are -no references to any specific hero question -outside the objects returned by `QuestionService`. - -This is very important since it allows you to repurpose the components for any type of survey +The example provides a model for a job application for heroes, but there are +no references to any specific hero question other than the objects returned by `QuestionService`. +This separation of model and data allows you to repurpose the components for any type of survey as long as it's compatible with the *question* object model. -The key is the dynamic data binding of metadata used to render the form + +### Ensuring valid data + +The form template uses dynamic data binding of metadata to render the form without making any hardcoded assumptions about specific questions. -In addition to control metadata, you are also adding validation dynamically. +It adds both control metadata and validation criteria dynamically. -The *Save* button is disabled until the form is in a valid state. +To ensure valid input, the *Save* button is disabled until the form is in a valid state. When the form is valid, you can click *Save* and the app renders the current form values as JSON. -This proves that any user input is bound back to the data model. -Saving and retrieving the data is an exercise for another time. - -The final form looks like this: +The following figure shows the final form. +## Next steps -[Back to top](guide/dynamic-form#top) +* **Different types of forms and control collection** + + This tutorial shows how to build a a questionaire, which is just one kind of dynamic form. + The example uses `FormGroup` to collect a set of controls. + 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. + That example also shows how to use `FormArray` instead of `FormGroup` to collect a set of controls. + +* **Validating user input** + + 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. + + The [Form validation guide](guide/form-validation "Form validation guide") covers the topic in more depth. diff --git a/aio/content/navigation.json b/aio/content/navigation.json index 8b8aee8a82..01d72d0d02 100644 --- a/aio/content/navigation.json +++ b/aio/content/navigation.json @@ -266,8 +266,8 @@ }, { "url": "guide/dynamic-form", - "title": "Dynamic Forms", - "tooltip": "Render dynamic forms with FormGroup." + "title": "Building Dynamic Forms", + "tooltip": "Create dynamic form templates using FormGroup." } ] },