docs(dynamic-forms): edit copy to guidelines standards (#3525)

This commit is contained in:
Kapunahele Wong 2017-04-11 22:17:23 -04:00 committed by Ward Bell
parent 0cf98324a8
commit 6a9007c65b
1 changed files with 81 additions and 70 deletions

View File

@ -1,50 +1,50 @@
include ../_util-fns include ../_util-fns
:marked :marked
We can't always justify the cost and time to build handcrafted forms, Building handcrafted forms can be costly and time-consuming,
especially if we'll need a great number of them, they're similar to each other, and they change frequently 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. to meet rapidly changing business and regulatory requirements.
It may be more economical to create the forms dynamically, based on metadata that describe the business object model. It may be more economical to create the forms dynamically, based on
metadata that describes the business object model.
In this cookbook we show how to use `formGroup` to dynamically render a simple form with different control types and validation.
It's a primitive start. 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. It might evolve to support a much richer variety of questions, more graceful rendering, and superior user experience.
All such greatness has humble beginnings. All such greatness has humble beginnings.
In our example we use a dynamic form to build an online application experience for heroes seeking employment. 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. The agency is constantly tinkering with the application process.
We can create the forms on the fly *without changing our application code*. You can create the forms on the fly *without changing the application code*.
<a id="toc"></a> <a id="toc"></a>
:marked :marked
## Table of contents # Contents
* [Bootstrap](#bootstrap)
[Bootstrap](#bootstrap) * [Question model](#object-model)
* [Question form components](#form-component)
[Question Model](#object-model) * [Questionnaire data](#questionnaire-data)
* [Dynamic template](#dynamic-template)
[Form Component](#form-component)
[Questionnaire Metadata](#questionnaire-metadata)
[Dynamic Template](#dynamic-template)
:marked :marked
**See the <live-example name="cb-dynamic-form"></live-example>**. See the <live-example name="cb-dynamic-form"></live-example>.
.l-main-section .l-main-section
<a id="bootstrap"></a> <a id="bootstrap"></a>
:marked :marked
## Bootstrap ## Bootstrap
We start by creating an `NgModule` called `AppModule`. Start by creating an `NgModule` called `AppModule`.
In our example we will be using Reactive Forms. This cookbook uses [reactive forms](../guide/reactive-forms.html).
Reactive Forms belongs to a different `NgModule` called `ReactiveFormsModule`, so in order to access any Reactive Forms directives, we have to import `ReactiveFormsModule` from the `@angular/forms` library.
We bootstrap our `AppModule` in main.ts. 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.
Bootstrap the `AppModule` in `main.ts`.
+makeTabs( +makeTabs(
`cb-dynamic-form/ts/src/app/app.module.ts, `cb-dynamic-form/ts/src/app/app.module.ts,
@ -52,58 +52,64 @@ include ../_util-fns
null, null,
`app.module.ts, `app.module.ts,
main.ts` main.ts`
) )
.l-main-section .l-main-section
<a id="object-model"></a> <a id="object-model"></a>
:marked :marked
## Question Model ## Question model
The next step is to define an object model that can describe all scenarios needed by the form functionality. 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 hero application process involves a form with a lot of questions.
The "question" is the most fundamental object in the model. The _question_ is the most fundamental object in the model.
We have created `QuestionBase` as the most fundamental question class. The following `QuestionBase` is a fundamental question class.
+makeExample('cb-dynamic-form/ts/src/app/question-base.ts','','src/app/question-base.ts') +makeExample('cb-dynamic-form/ts/src/app/question-base.ts','','src/app/question-base.ts')
:marked :marked
From this base we derived two new classes in `TextboxQuestion` and `DropdownQuestion` that represent Textbox and Dropdown questions. From this base you can derive two new classes in `TextboxQuestion` and `DropdownQuestion`
The idea is that the form will be bound to specific question types and render the appropriate controls dynamically. that represent textbox and dropdown questions.
The idea is that the form will be bound to specific question types and render the
`TextboxQuestion` supports multiple html5 types like text, email, url etc via the `type` property. appropriate controls dynamically.
`TextboxQuestion` supports multiple HTML5 types such as text, email, and url
via the `type` property.
+makeExample('cb-dynamic-form/ts/src/app/question-textbox.ts',null,'src/app/question-textbox.ts')(format='.') +makeExample('cb-dynamic-form/ts/src/app/question-textbox.ts',null,'src/app/question-textbox.ts')(format='.')
:marked :marked
`DropdownQuestion` presents a list of choices in a select box. `DropdownQuestion` presents a list of choices in a select box.
+makeExample('cb-dynamic-form/ts/src/app/question-dropdown.ts',null,'src/app/question-dropdown.ts')(format='.') +makeExample('cb-dynamic-form/ts/src/app/question-dropdown.ts',null,'src/app/question-dropdown.ts')(format='.')
:marked :marked
Next we have defined `QuestionControlService`, a simple service for transforming our questions to a `FormGroup`. 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 us to specify default values and validation rules. In a nutshell, the form group consumes the metadata from the question model and
allows you to specify default values and validation rules.
+makeExample('cb-dynamic-form/ts/src/app/question-control.service.ts',null,'src/app/question-control.service.ts')(format='.') +makeExample('cb-dynamic-form/ts/src/app/question-control.service.ts',null,'src/app/question-control.service.ts')(format='.')
<a id="form-component"></a> <a id="form-component"></a>
:marked :marked
## Question form components ## Question form components
Now that we have defined the complete model we are ready to create components to represent the dynamic form. Now that you have defined the complete model you are ready
to create components to represent the dynamic form.
:marked :marked
`DynamicFormComponent` is the entry point and the main container for the form. `DynamicFormComponent` is the entry point and the main container for the form.
+makeTabs( +makeTabs(
`cb-dynamic-form/ts/src/app/dynamic-form.component.html, `cb-dynamic-form/ts/src/app/dynamic-form.component.html,
cb-dynamic-form/ts/src/app/dynamic-form.component.ts`, cb-dynamic-form/ts/src/app/dynamic-form.component.ts`,
null, null,
`dynamic-form.component.html, `dynamic-form.component.html,
dynamic-form.component.ts` dynamic-form.component.ts`
) )
:marked :marked
It presents a list of questions, each question bound to a `<df-question>` component element. It presents a list of questions, each bound to a `<df-question>` component element.
The `<df-question>` tag matches the `DynamicFormQuestionComponent`, The `<df-question>` 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 component responsible for rendering the details of each _individual_
question based on values in the data-bound question object.
+makeTabs( +makeTabs(
`cb-dynamic-form/ts/src/app/dynamic-form-question.component.html, `cb-dynamic-form/ts/src/app/dynamic-form-question.component.html,
@ -113,48 +119,53 @@ include ../_util-fns
dynamic-form-question.component.ts` dynamic-form-question.component.ts`
) )
:marked :marked
Notice this component can present any type of question in our model. Notice this component can present any type of question in your model.
We only have two types of questions at this point but we can imagine many more. 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` determines which type of question to display.
In both components we're relying on Angular's **formGroup** to connect the template HTML to the 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. underlying control objects, populated from the question model with display and validation rules.
`formControlName` and `formGroup` are directives defined in `ReactiveFormsModule`. Our templates can access these directives directly since we imported `ReactiveFormsModule` from `AppModule`. `formControlName` and `formGroup` are directives defined in
`ReactiveFormsModule`. The templates can access these directives
directly since you imported `ReactiveFormsModule` from `AppModule`.
<a id="questionnaire-metadata"></a> <a id="questionnaire-data"></a>
:marked :marked
## Questionnaire data ## Questionnaire data
:marked :marked
`DynamicFormComponent` expects the list of questions in the form of an array bound to `@Input() questions`. `DynamicFormComponent` expects the list of questions in the form of an array bound to `@Input() questions`.
The set of questions we have defined for the job application is returned from the `QuestionService`. The set of questions you've defined for the job application is returned from the `QuestionService`.
In a real app we'd retrieve these questions from storage. In a real app you'd retrieve these questions from storage.
The key point is that we control the hero job application questions entirely through the objects returned from `QuestionService`. The key point is that you control the hero job application questions
Questionnaire maintenance is a simple matter of adding, updating, and removing objects from the `questions` array. entirely through the objects returned from `QuestionService`.
Questionnaire maintenance is a simple matter of adding, updating,
+makeExample('cb-dynamic-form/ts/src/app/question.service.ts','','src/app/question.service.ts') and removing objects from the `questions` array.
+makeExample('cb-dynamic-form/ts/src/app/question.service.ts','','src/app/question.service.ts')
:marked :marked
Finally, we display an instance of the form in the `AppComponent` shell. Finally, display an instance of the form in the `AppComponent` shell.
+makeExample('cb-dynamic-form/ts/src/app/app.component.ts','','app.component.ts') +makeExample('cb-dynamic-form/ts/src/app/app.component.ts','','app.component.ts')
<a id="dynamic-template"></a> <a id="dynamic-template"></a>
:marked :marked
## Dynamic Template ## Dynamic Template
Although in this example we're modelling a job application for heroes, there are no references to any specific hero question Although in this example you're modelling a job application for heroes, there are
outside the objects returned by `QuestionService`. no references to any specific hero question
outside the objects returned by `QuestionService`.
This is very important since it allows us to repurpose the components for any type of survey
as long as it's compatible with our *question* object model.
The key is the dynamic data binding of metadata used to render the form
without making any hardcoded assumptions about specific questions.
In addition to control metadata, we are also adding validation dynamically.
The *Save* button is disabled until the form is in a valid state. This is very important since it allows you to repurpose the components for any type of survey
When the form is valid, we can click *Save* and the app renders the current form values as JSON. 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
without making any hardcoded assumptions about specific questions.
In addition to control metadata, you are also adding validation dynamically.
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. This proves that any user input is bound back to the data model.
Saving and retrieving the data is an exercise for another time. Saving and retrieving the data is an exercise for another time.