| 
									
										
										
										
											2019-12-02 12:47:19 -08:00
										 |  |  | # Dynamic forms
 | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-30 00:03:22 +08:00
										 |  |  | # 动态表单
 | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-30 22:10:32 +02:00
										 |  |  | {@a top} | 
					
						
							| 
									
										
										
										
											2017-04-01 01:57:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-03 21:06:01 +08:00
										 |  |  | 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 | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | to meet rapidly changing business and regulatory requirements. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 有时候手动编写和维护表单所需工作量和时间会过大。特别是在需要编写大量表单时。表单都很相似,而且随着业务和监管需求的迅速变化,表单也要随之变化,这样维护的成本过高。 | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-03 21:06:01 +08:00
										 |  |  | It may be more economical to create the forms dynamically, based on | 
					
						
							|  |  |  | metadata that describes the business object model. | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-28 11:54:59 +08:00
										 |  |  | 基于业务对象模型的元数据,动态创建表单可能会更划算。 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-03 21:06:01 +08:00
										 |  |  | This cookbook shows you how to use `formGroup` to dynamically | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | render a simple form with different control types and validation. | 
					
						
							|  |  |  | It's a primitive start. | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | It might evolve to support a much richer variety of questions, more graceful rendering, and superior user experience. | 
					
						
							|  |  |  | All such greatness has humble beginnings. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-22 17:18:48 +08:00
										 |  |  | 本文会展示如何利用 `formGroup` 来动态渲染一个简单的表单,包括各种控件类型和验证规则。 | 
					
						
							| 
									
										
										
										
											2017-08-28 11:54:59 +08:00
										 |  |  | 这个起点很简陋,但可以在这个基础上添加丰富多彩的问卷问题、更优美的渲染以及更卓越的用户体验。 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-03 21:06:01 +08:00
										 |  |  | The example in this cookbook is a dynamic form to build an | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | online application experience for heroes seeking employment. | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | The agency is constantly tinkering with the application process. | 
					
						
							| 
									
										
										
										
											2018-03-03 21:06:01 +08:00
										 |  |  | You can create the forms on the fly *without changing the application code*. | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-22 17:18:48 +08:00
										 |  |  | 这个例子要为正在找工作的英雄们创建一个在线申请表的动态表单。英雄管理局会不断修改申请流程,你要在*不修改应用代码*的情况下,动态创建这些表单。 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-01 01:57:13 +02:00
										 |  |  | {@a toc} | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-21 17:21:45 -07:00
										 |  |  | See the <live-example name="dynamic-form"></live-example>. | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-30 00:03:22 +08:00
										 |  |  | 参见<live-example name="dynamic-form"></live-example>。 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-01 01:57:13 +02:00
										 |  |  | {@a bootstrap} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## Bootstrap
 | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-03 21:06:01 +08:00
										 |  |  | ## 启动/引导 (bootstrap)
 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | Start by creating an `NgModule` called `AppModule`. | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-22 17:18:48 +08:00
										 |  |  | 从创建一个名叫 `AppModule` 的 `NgModule` 开始。 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | This cookbook uses [reactive forms](guide/reactive-forms). | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 这个烹饪书使用[响应式表单](guide/reactive-forms)。 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 15:11:02 +03:00
										 |  |  | Reactive forms belongs to a different `NgModule` called `ReactiveFormsModule`, | 
					
						
							|  |  |  | so in order to access any reactive forms directives, you have to import | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | `ReactiveFormsModule` from the `@angular/forms` library. | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-22 17:18:48 +08:00
										 |  |  | 响应式表单属于另外一个叫做 `ReactiveFormsModule` 的 `NgModule`,所以,为了使用响应式表单类的指令,你得从 `@angular/forms` 库中引入 `ReactiveFormsModule` 模块。 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | Bootstrap the `AppModule` in `main.ts`. | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-22 17:18:48 +08:00
										 |  |  | 在 `main.ts` 中启动 `AppModule`。 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-27 16:08:53 +01:00
										 |  |  | <code-tabs> | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-11 13:29:59 +02:00
										 |  |  |   <code-pane header="app.module.ts" path="dynamic-form/src/app/app.module.ts"> | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-27 16:08:53 +01:00
										 |  |  |   </code-pane> | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-11 13:29:59 +02:00
										 |  |  |   <code-pane header="main.ts" path="dynamic-form/src/main.ts"> | 
					
						
							| 
									
										
										
										
											2017-03-27 16:08:53 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |   </code-pane> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | </code-tabs> | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-01 01:57:13 +02:00
										 |  |  | {@a object-model} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | ## Question model
 | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | ## 问卷问题模型
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | The next step is to define an object model that can describe all scenarios needed by the form functionality. | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | The hero application process involves a form with a lot of questions. | 
					
						
							|  |  |  | The _question_ is the most fundamental object in the model. | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-26 22:58:06 +08:00
										 |  |  | 下一步是定义一个对象模型,用来描述所有表单功能需要的场景。英雄的申请流程涉及到一个包含很多问卷问题的表单。问卷问题是最基础的对象模型。 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | The following `QuestionBase` is a fundamental question class. | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-22 17:18:48 +08:00
										 |  |  | 下面的 `QuestionBase` 是最基础的问卷问题基类。 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-11 13:29:59 +02:00
										 |  |  | <code-example path="dynamic-form/src/app/question-base.ts" header="src/app/question-base.ts"> | 
					
						
							| 
									
										
										
										
											2017-03-27 16:08:53 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | </code-example> | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 15:11:02 +03:00
										 |  |  | From this base you can derive two new classes in `TextboxQuestion` and `DropdownQuestion` | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | that represent textbox and dropdown questions. | 
					
						
							| 
									
										
										
										
											2017-04-26 15:11:02 +03:00
										 |  |  | The idea is that the form will be bound to specific question types and render the | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | appropriate controls dynamically. | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-22 17:18:48 +08:00
										 |  |  | 在这个基础上,你派生出两个新类 `TextboxQuestion` 和 `DropdownQuestion`,分别代表文本框和下拉框。这么做的初衷是,表单能动态绑定到特定的问卷问题类型,并动态渲染出合适的控件。 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 15:11:02 +03:00
										 |  |  | `TextboxQuestion` supports multiple HTML5 types such as text, email, and url | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | via the `type` property. | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-20 17:09:18 +08:00
										 |  |  | `TextboxQuestion` 可以通过 `type` 属性来支持多种 HTML5 元素类型,比如文本、邮件、网址等。 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-20 20:40:17 +03:00
										 |  |  | <code-example path="dynamic-form/src/app/question-textbox.ts" header="src/app/question-textbox.ts"></code-example> | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | `DropdownQuestion` presents a list of choices in a select box. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-20 17:09:18 +08:00
										 |  |  | `DropdownQuestion` 表示一个带可选项列表的选择框。 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-20 20:40:17 +03:00
										 |  |  | <code-example path="dynamic-form/src/app/question-dropdown.ts" header="src/app/question-dropdown.ts"></code-example> | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | Next is `QuestionControlService`, a simple service for transforming the questions to a `FormGroup`. | 
					
						
							| 
									
										
										
										
											2017-04-26 15:11:02 +03:00
										 |  |  | In a nutshell, the form group consumes the metadata from the question model and | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | allows you to specify default values and validation rules. | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-22 17:18:48 +08:00
										 |  |  | 接下来定义了 `QuestionControlService`,一个可以把问卷问题转换为 `FormGroup` 的服务。 | 
					
						
							|  |  |  | 简而言之,这个 `FormGroup` 使用问卷模型的元数据,并允许你指定默认值和验证规则。 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-20 20:40:17 +03:00
										 |  |  | <code-example path="dynamic-form/src/app/question-control.service.ts" header="src/app/question-control.service.ts"></code-example> | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-01 01:57:13 +02:00
										 |  |  | {@a form-component} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## Question form components
 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | ## 问卷表单组件
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 15:11:02 +03:00
										 |  |  | Now that you have defined the complete model you are ready | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | to create components to represent the dynamic form. | 
					
						
							| 
									
										
										
										
											2017-04-01 01:57:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-22 17:18:48 +08:00
										 |  |  | 现在你已经有一个定义好的完整模型了,接着就可以开始创建一个展现动态表单的组件。 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-03 21:06:01 +08:00
										 |  |  | `DynamicFormComponent` is the entry point and the main container for the form. | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-20 17:09:18 +08:00
										 |  |  | `DynamicFormComponent` 是表单的主要容器和入口点。 | 
					
						
							| 
									
										
										
										
											2017-04-01 01:57:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-27 16:08:53 +01:00
										 |  |  | <code-tabs> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-11 13:29:59 +02:00
										 |  |  |   <code-pane header="dynamic-form.component.html" path="dynamic-form/src/app/dynamic-form.component.html"> | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-27 16:08:53 +01:00
										 |  |  |   </code-pane> | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-11 13:29:59 +02:00
										 |  |  |   <code-pane header="dynamic-form.component.ts" path="dynamic-form/src/app/dynamic-form.component.ts"> | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-27 16:08:53 +01:00
										 |  |  |   </code-pane> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | </code-tabs> | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-10 10:45:05 +01:00
										 |  |  | It presents a list of questions, each bound to a `<app-question>` component element. | 
					
						
							|  |  |  | The `<app-question>` tag matches the `DynamicFormQuestionComponent`, | 
					
						
							| 
									
										
										
										
											2017-04-26 15:11:02 +03:00
										 |  |  | the component responsible for rendering the details of each _individual_ | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | question based on values in the data-bound question object. | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-20 17:09:18 +08:00
										 |  |  | 它代表了问卷问题列表,每个问题都被绑定到一个 `<app-question>` 组件元素。 | 
					
						
							|  |  |  | `<app-question>` 标签匹配到的是组件 `DynamicFormQuestionComponent`,该组件的职责是根据各个问卷问题对象的值来动态渲染表单控件。 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-27 16:08:53 +01:00
										 |  |  | <code-tabs> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-11 13:29:59 +02:00
										 |  |  |   <code-pane header="dynamic-form-question.component.html" path="dynamic-form/src/app/dynamic-form-question.component.html"> | 
					
						
							| 
									
										
										
										
											2017-03-27 16:08:53 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |   </code-pane> | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-11 13:29:59 +02:00
										 |  |  |   <code-pane header="dynamic-form-question.component.ts" path="dynamic-form/src/app/dynamic-form-question.component.ts"> | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-27 16:08:53 +01:00
										 |  |  |   </code-pane> | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-27 16:08:53 +01:00
										 |  |  | </code-tabs> | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | Notice this component can present any type of question in your model. | 
					
						
							|  |  |  | You only have two types of questions at this point but you can imagine many more. | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | The `ngSwitch` determines which type of question to display. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-20 17:09:18 +08:00
										 |  |  | 请注意,这个组件能代表模型里的任何问题类型。目前,还只有两种问题类型,但可以添加更多类型。可以用 `ngSwitch` 决定显示哪种类型的问题。 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-26 21:35:03 +07:00
										 |  |  | In both components you're relying on Angular's **formGroup** to connect the template HTML to the | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | underlying control objects, populated from the question model with display and validation rules. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-22 17:18:48 +08:00
										 |  |  | 在这两个组件中,你依赖 Angular 的 **formGroup** 来把模板 HTML 和底层控件对象连接起来,该对象从问卷问题模型里获取渲染和验证规则。 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 15:11:02 +03:00
										 |  |  | `formControlName` and `formGroup` are directives defined in | 
					
						
							|  |  |  | `ReactiveFormsModule`. The templates can access these directives | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | directly since you imported `ReactiveFormsModule` from `AppModule`. | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-22 17:18:48 +08:00
										 |  |  | `formControlName` 和 `formGroup` 是在 `ReactiveFormsModule` 中定义的指令。这个模板之所以能使用它们,是因为你曾从 `AppModule` 中导入了 `ReactiveFormsModule`。 | 
					
						
							| 
									
										
										
										
											2018-03-03 21:06:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | {@a questionnaire-data} | 
					
						
							| 
									
										
										
										
											2017-04-01 01:57:13 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | ## Questionnaire data
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | ## 问卷数据
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | `DynamicFormComponent` expects the list of questions in the form of an array bound to `@Input() questions`. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-02 02:29:31 +08:00
										 |  |  | `DynamicFormComponent` 期望得到一个问题列表,该数组被绑定到 `@Input() questions` 属性。 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  |  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. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-22 17:18:48 +08:00
										 |  |  |  `QuestionService` 会返回为工作申请表定义的那组问题列表。在真实的应用程序环境中,你会从数据库里获得这些问题列表。 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 15:11:02 +03:00
										 |  |  |  The key point is that you control the hero job application questions | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  |  entirely through the objects returned from `QuestionService`. | 
					
						
							| 
									
										
										
										
											2017-04-26 15:11:02 +03:00
										 |  |  |  Questionnaire maintenance is a simple matter of adding, updating, | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  |  and removing objects from the `questions` array. | 
					
						
							| 
									
										
										
										
											2018-03-03 21:06:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-22 17:18:48 +08:00
										 |  |  |  关键是,你完全根据 `QuestionService` 返回的对象来控制英雄的工作申请表。 | 
					
						
							| 
									
										
										
										
											2018-08-08 11:14:02 +08:00
										 |  |  |  要维护这份问卷,只要非常简单地添加、修改和删除 `questions` 数组中的对象就可以了。 | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-11 13:29:59 +02:00
										 |  |  | <code-example path="dynamic-form/src/app/question.service.ts" header="src/app/question.service.ts"> | 
					
						
							| 
									
										
										
										
											2017-03-27 16:08:53 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | </code-example> | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | Finally, display an instance of the form in the `AppComponent` shell. | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-20 17:09:18 +08:00
										 |  |  | 最后,在 `AppComponent` 里显示出表单。 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-11 13:29:59 +02:00
										 |  |  | <code-example path="dynamic-form/src/app/app.component.ts" header="app.component.ts"> | 
					
						
							| 
									
										
										
										
											2017-03-27 16:08:53 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | </code-example> | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-01 01:57:13 +02:00
										 |  |  | {@a dynamic-template} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## Dynamic Template
 | 
					
						
							| 
									
										
										
										
											2017-08-06 13:21:34 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | ## 动态模板
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-03 21:06:01 +08:00
										 |  |  | Although in this example you're modelling a job application for heroes, there are | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | no references to any specific hero question | 
					
						
							|  |  |  | outside the objects returned by `QuestionService`. | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-22 17:18:48 +08:00
										 |  |  | 在这个例子中,虽然你是在为英雄的工作申请表建模,但是除了 `QuestionService` 返回的那些对象外,没有其它任何地方是与英雄有关的。 | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | This is very important since it allows you to repurpose the components for any type of survey | 
					
						
							| 
									
										
										
										
											2018-03-03 21:06:01 +08:00
										 |  |  | 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. | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | In addition to control metadata, you are also adding validation dynamically. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 这点非常重要,因为只要与*问卷*对象模型兼容,就可以在任何类型的调查问卷中复用这些组件。 | 
					
						
							|  |  |  | 这里的关键是用到元数据的动态数据绑定来渲染表单,对问卷问题没有任何硬性的假设。除控件的元数据外,还可以动态添加验证规则。 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 21:53:18 +02:00
										 |  |  | 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. | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | This proves that any user input is bound back to the data model. | 
					
						
							|  |  |  | Saving and retrieving the data is an exercise for another time. | 
					
						
							| 
									
										
										
										
											2017-04-01 01:57:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-20 17:09:18 +08:00
										 |  |  | 表单验证通过之前,*保存*按钮是禁用的。验证通过后,就可以点击*保存*按钮,程序会把当前值渲染成 JSON 显示出来。 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 这表明任何用户输入都被传到了数据模型里。至于如何储存和提取数据则是另一话题了。 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-22 18:09:39 +00:00
										 |  |  | The final form looks like this: | 
					
						
							| 
									
										
										
										
											2017-03-30 20:04:18 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | 完整的表单是这样的: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-11 14:47:51 -08:00
										 |  |  | <div class="lightbox"> | 
					
						
							| 
									
										
										
										
											2018-03-03 21:06:01 +08:00
										 |  |  |   <img src="generated/images/guide/dynamic-form/dynamic-form.png" alt="Dynamic-Form"> | 
					
						
							| 
									
										
										
										
											2019-11-11 14:47:51 -08:00
										 |  |  | </div> | 
					
						
							| 
									
										
										
										
											2017-04-01 01:57:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-23 11:51:25 +08:00
										 |  |  | [Back to top](guide/dynamic-form#top) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-07 11:25:56 +08:00
										 |  |  | [回到顶部](guide/dynamic-form#top) |