使用ng2-admin搭建成熟可靠的後臺系統 -- ng2-admin(二)

使用ng2-admin搭建成熟可靠的後臺系統 -- ng2-admin(二)

1.構建動態表單

  • 構建一個動態表單,動態生成控件,驗證規則。
  • 建立一個input組件,一個select組件
  • 將組件注入到頁面中並顯示


建立組件目錄

前半部分直接借鑑官方文檔的寫法,後期開啓個性化定製後,功能強大!

theme/components 目錄下建立一個文件夾 dynamic-form html

項目目錄結構以下,下面跟着步驟一步一步來構建typescript

mark

建立對象模型

咱們須要定義一個對象模型,用來描述表單功能須要的場景。相關功能很是之多(相似於 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 || '';
    }
}

在這個基礎上,咱們派生出兩個類 InputQuestionSelectQuestion ,表明文本框和下拉框。瀏覽器

這麼作的緣由是根據不一樣的控件,進行個性化定製,以及合理規範,還有動態渲染出合適的組件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 已經幫咱們註冊好了,因此咱們這裏只須要註冊咱們建立的組件便可

方法以下圖,先引入

mark

而後添加至 NGA_COMPONENTS

mark

註冊Service

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中,以下圖
mark

而後打開瀏覽器,輸入 http://localhost:4200/#/pages/user/list/add

訪問效果以下圖,填入一些數據,而後點擊保存,咱們須要存入的數據,顯示在了下方

mark

動態表單的雛形已經作出來了,如今還有幾個問題

  • 樣式須要優化
  • 數據如何過濾優化
  • 數據如何提交
  • 組件功能有些薄弱

這些問題咱們會在後續的章節慢慢解決,能夠期待。

相關文章
相關標籤/搜索