美文网首页angular2Angular 4.x 修仙之路
使用ng2-admin搭建成熟可靠的后台系统 -- ng2-ad

使用ng2-admin搭建成熟可靠的后台系统 -- ng2-ad

作者: 昵称不用太拉风 | 来源:发表于2017-09-27 19:06 被阅读101次

    使用ng2-admin搭建成熟可靠的后台系统 -- ng2-admin(二)

    1.构建动态表单

    • 构建一个动态表单,动态生成控件,验证规则。

    • 创建一个input组件,一个select组件

    • 将组件注入到页面中并显示

    创建组件目录

    前半部分直接借鉴官方文档的写法,后期开启个性化定制后,功能强大!

    theme/components 目录下创建一个文件夹 dynamic-form

    项目目录结构如下,下面跟着步骤一步一步来构建

    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 ,代表文本框和下拉框。

    这么做的原因是根据不同的控件,进行个性化定制,以及合理规范,还有动态渲染出合适的组件

    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 表示一个带可选列表的选择框

    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 将这三个模型导出

    接下来,我们定义一个 QuestionControlService,一个可以把模型转换为FormGroup的服务。 简而言之,这个FormGroup使用问卷模型的元数据,并允许我们设置默认值和验证规则

    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

    动态表单的雏形已经做出来了,现在还有几个问题

    • 样式需要优化

    • 数据如何过滤优化

    • 数据如何提交

    • 组件功能有些薄弱

    这些问题我们会在后续的章节慢慢解决,可以期待。

    相关文章

      网友评论

        本文标题:使用ng2-admin搭建成熟可靠的后台系统 -- ng2-ad

        本文链接:https://www.haomeiwen.com/subject/qexpextx.html