- 構建一個動態表單,動態生成控件,驗證規則。
- 建立一個input組件,一個select組件
- 將組件注入到頁面中並顯示
前半部分直接借鑑官方文檔的寫法,後期開啓個性化定製後,功能強大!
在 theme/components
目錄下建立一個文件夾 dynamic-form
html
項目目錄結構以下,下面跟着步驟一步一步來構建typescript
咱們須要定義一個對象模型,用來描述表單功能須要的場景。相關功能很是之多(相似於 input
select
)。數據庫
先建立一個最基礎的基類,名爲 QuestionBase
,文件爲 dynamic-form-base/question-base.ts
數組
export class QuestionBase<T>{ value: T; // 值,類型可選 key: string; // 字段名 label: string; // 控件前的文字提示 required: boolean; // 是否爲必填 disabled: boolean; // 是否禁用 reg: string; // 正則 prompt: string; // 驗證不經過提示 order: number; // 排序 controlType: string; // 渲染類型 constructor(options: { value?: T, key?: string, label?: string, required?: boolean, disabled?: boolean, reg?: string, prompt?: string, order?: number, controlType?: string } = {}) { // 設置各個值的默認值 this.value = options.value; this.key = options.key || ''; this.label = options.label || ''; this.required = !!options.required; this.disabled = !!options.disabled; this.reg = options.reg || ''; this.prompt = options.prompt || ''; this.order = options.order || 0; this.controlType = options.controlType || ''; } }
在這個基礎上,咱們派生出兩個類 InputQuestion
和 SelectQuestion
,表明文本框和下拉框。瀏覽器
這麼作的緣由是根據不一樣的控件,進行個性化定製,以及合理規範,還有動態渲染出合適的組件ide
InputQuestion
能夠經過type屬性來支持多種HTML元素類型(例如: text number email
) --- dynamic-form-base/question-input.ts
優化
import { QuestionBase } from './question-base'; export class InputQuestion extends QuestionBase<string | number> { controlType = 'input'; type: string; constructor(options: {} = {}) { super(options); this.type = options['type'] || ''; } }
SelectQuestion
表示一個帶可選列表的選擇框ui
import { QuestionBase } from './question-base'; export class SelectQuestion extends QuestionBase<string | number> { controlType = 'select'; options: any[] = []; constructor(options: {} = {}) { super(options); this.options = options['options'] || []; } }
最後別忘了用 index.ts
將這三個模型導出this
接下來,咱們定義一個 QuestionControlService
,一個能夠把模型轉換爲FormGroup的服務。 簡而言之,這個FormGroup使用問卷模型的元數據,並容許咱們設置默認值和驗證規則spa
question-control.service.ts
import { Injectable } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { QuestionBase } from './dynamic-form-base'; @Injectable() export class QuestionControlService { constructor() { } // 轉化爲控件 toFormGroup(questions: QuestionBase<any>[]) { let group: any = {}; questions.forEach(question => { if (question.required) { // 選項爲必填時 if (question.reg) { // 有正則的狀況 group[question.key] = new FormControl(question.value || '', Validators.compose([Validators.required, Validators.pattern(question.reg)])); } else { group[question.key] = new FormControl(question.value || '', Validators.compose([Validators.required])); } } else if (!question.required && question.reg) { // 選項爲非必填可是須要正則匹配的狀況 group[question.key] = new FormControl(question.value || '', Validators.compose([Validators.pattern(question.reg)])); } else { group[question.key] = new FormControl(question.value || ''); } }); return new FormGroup(group); } }
如今咱們已經有了一個定義好的完整模型了,接着就能夠開始構建一個展示動態表單的組件。
DynamicFormComponent
是表單的主要容器和入口
dynamic-form.component.html
<div> <form (ngSubmit)="onSubmit()" [formGroup]="form"> <div *ngFor="let question of questions" class="form-row"> <df-question [question]="question" [form]="form"></df-question> </div> <div class="form-row"> <button type="submit" [disabled]="!form.valid">保存</button> </div> </form> <div *ngIf="payload" class="form-row"> <strong>Saved the following values</strong><br>{{payload}} </div> </div>
dynamic-form.component.ts
import { Component, Input, OnInit } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { QuestionBase } from './dynamic-form-base/question-base'; import { QuestionControlService } from './question-control.service'; @Component({ selector: 'dynamic-form', templateUrl: './dynamic-form.component.html', providers: [QuestionControlService] }) export class DynamicFormComponent implements OnInit { @Input() questions: QuestionBase<any>[] = []; form: FormGroup; payload = ''; constructor( private qcs: QuestionControlService ) { } ngOnInit() { console.log(this.questions); this.form = this.qcs.toFormGroup(this.questions); console.log(this.form); } onSubmit() { this.payload = JSON.stringify(this.form.value); } }
剛纔建立的是一個組件入口,每一個 question
都被綁定到了 <df-question>
組件元素,<df-question>
標籤匹配到的是組件 DynamicFormQuestionComponent
,該組件的職責是根據各個問卷問題對象的值來動態渲染表單控件。
如今來建立它的子組件 DynamicFormQuestionComponent
dynamic-form-question/dynamic-form-question.component.html
<div [formGroup]="form"> <div [ngSwitch]="question.controlType"> <input *ngSwitchCase="'input'" [formControlName]="question.key" [id]="question.key" [type]="question.type"> <select [id]="question.key" *ngSwitchCase="'select'" [formControlName]="question.key"> <option *ngFor="let opt of question.options" [value]="opt.value">{{opt.key}}</option> </select> </div> </div>
dynamic-form-question/dynamic-form-question.component.ts
import { Component, Input } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { QuestionBase } from '../dynamic-form-base/question-base'; @Component({ selector: 'df-question', templateUrl: './dynamic-form-question.component.html' }) export class DynamicFormQuestionComponent { @Input() question: QuestionBase<any>; @Input() form: FormGroup; get isValid() { return this.form.controls[this.question.key].valid } }
從上面的組件能夠看出,將來須要添加組件時,只須要添加一種類型,能夠用 ngSwitch
決定顯示哪一種類型的問題。
在這兩個組件中,咱們依賴Angular的formGroup來把模板HTML和底層控件對象鏈接起來,該對象從問卷問題模型裏獲取渲染和驗證規則。
注意:每一個目錄都須要用index.ts
導出模塊, 這裏須要在theme/components
將咱們註冊的組件統一導出。
咱們建立組件以後,須要將咱們的組件註冊到 module
中,這裏選擇 theme/nga.module.ts
注入咱們的組件。
其實使用組件還須要注入
ReactiveFormsModule
, ng2-admin 已經幫咱們註冊好了,因此咱們這裏只須要註冊咱們建立的組件便可
方法以下圖,先引入
而後添加至 NGA_COMPONENTS
DynamicForm
指望獲得一個問題列表,該列表被綁定到@Input() questions屬性。
QuestionService
會返回爲工做申請表定義的那組問題列表。在真實的應用程序環境中,咱們會從數據庫裏得到這些問題列表。
要維護控件,只要很是簡單的添加、更新和刪除 questions
數組中的對象就能夠了。
切換到 pages
文件夾,開始使用咱們上一章建立的 UserAddComponent
pages/user/user-list/user-add/user-add.service.ts
import { Injectable } from '@angular/core'; import { QuestionBase, InputQuestion, SelectQuestion } from '../../../../theme/components/dynamic-form/dynamic-form-base'; @Injectable() export class UserAddService { getQuestions() { let questions: QuestionBase<any>[] = [ new SelectQuestion({ key: 'brave', label: 'Bravery Rating', value: 'solid', options: [ { key: 'Solid', value: 'solid' }, { key: 'Great', value: 'great' }, { key: 'Good', value: 'good' }, { key: 'Unproven', value: 'unproven' } ], order: 3 }), new InputQuestion({ key: 'firstName', label: 'First name', value: 'Bombasto', required: true, order: 1 }), new InputQuestion({ key: 'emailAddress', label: 'Email', type: 'email', order: 2 }) ]; return questions.sort((a, b) => a.order - b.order); } }
須要在 html
文件以及 component
文件中顯示,因此修改一下這兩個文件
user-add.component.html
<h1> 新增用戶組件 </h1> <div class="user-form"> <dynamic-form [questions]="UserAddQuestions"></dynamic-form> </div>
user-add.component.ts
import { Component } from '@angular/core'; import { UserAddService } from './user-add.service'; import { QuestionBase } from '../../../../theme/components/dynamic-form/dynamic-form-base/question-base'; @Component({ selector: 'ngt-user-add', templateUrl: './user-add.component.html', providers: [UserAddService] }) export class UserAddComponent { public UserAddQuestions: QuestionBase<any>[] = []; constructor( private service: UserAddService ) { this.UserAddQuestions = this.service.getQuestions(); } }
根據 Angular 的模塊查找規則,因此這裏還須要把 NgaModule 注入到 user.module.ts
中,以下圖
而後打開瀏覽器,輸入 http://localhost:4200/#/pages/user/list/add
訪問效果以下圖,填入一些數據,而後點擊保存,咱們須要存入的數據,顯示在了下方
動態表單的雛形已經作出來了,如今還有幾個問題
這些問題咱們會在後續的章節慢慢解決,能夠期待。