5

前言

之前已经介绍过:

  • 动态表单的构建
  • 动态表单的数据库的结构
  • 如何将数据库中的数据渲染出表单

具体可以参考之前两篇文章,本文内容如下:

  • 显示动态表单的首页列表
  • 数据的增改

效果演示:

stackBlitz: https://stackblitz.com/github/chshihang/dynamic-form-index?fi...

1、申请类型不同,列表显示字段不同:

2、不同的申请类型,新增字段不同:

3、编辑与新增


项目结构:

实例中以AppComponent作为着陆组件,index、add、edit分别用来显示首页列表,新增与编辑。DynamicFormService的部分功能为模拟数据库。

├── MockData.ts        预定义数据
├── add
│   ├── add.component.html
│   └── add.component.ts
├── app-routing.module.ts
├── app.component.html
├── app.component.ts
├── app.module.ts
├── dynamic-form
│   ├── dynamic-form.module.ts
│   ├── entity
│   │   ├── apply-type.ts
│   │   ├── apply.ts
│   │   ├── data-item.ts
│   │   ├── data-set.ts
│   │   ├── field-record.ts
│   │   ├── field-type.ts
│   │   ├── field-validator.ts
│   │   ├── field.ts
│   │   ├── form-info.ts
│   │   ├── rule-type.ts
│   │   └── table-label.ts
│   ├── field
│   │   ├── components
│   │   │   ├── control-type-list.config.ts
│   │   │   ├── dynamic-checkbox
│   │   │   │   ├── dynamic-checkbox.component.html
│   │   │   │   └── dynamic-checkbox.component.ts
│   │   │   ├── dynamic-dropdown
│   │   │   │   ├── dynamic-dropdown.component.html
│   │   │   │   └── dynamic-dropdown.component.ts
│   │   │   ├── dynamic-error
│   │   │   │   ├── dynamic-error.component.html
│   │   │   │   └── dynamic-error.component.ts
│   │   │   ├── dynamic-radio
│   │   │   │   ├── dynamic-radio.component.html
│   │   │   │   └── dynamic-radio.component.ts
│   │   │   └── dynamic-textbox
│   │   │       ├── dynamic-textbox.component.html
│   │   │       └── dynamic-textbox.component.ts
│   │   ├── field.component.html
│   │   ├── field.component.ts
│   │   ├── interface
│   │   │   ├── dynamic-form.interface.ts
│   │   │   └── dynamic-service.interface.ts
│   │   └── services
│   │       ├── dynamic-checkbox.service.ts        获取value(首页列表展示)
│   │       ├── dynamic-dropdown.service.ts        获取value(首页列表展示)
│   │       ├── dynamic-form.service.ts        转换数据/充当数据库
│   │       ├── dynamic-radio.service.ts     获取value(首页列表展示)
│   │       └── dynamic-textbox.service.ts    获取value(首页列表展示)
│   └── form
│       ├── form.component.html
│       └── form.component.ts
├── edit
│   ├── edit.component.html
│   └── edit.component.ts
└── index
    ├── index.component.html
    └── index.component.ts

ER图:

image.png

一、首页列表显示

1、获取申请类型以及申请

想要展示数据就需要先获取数据,但是因为有不同的申请类型,针对每一种申请类型要显示的内容都是不同的,所以需要先获取申请类型,此项通过用户选择来获取。即从数据库(DynamicFormService)获取所有类型并让用户选择。有了数据类型之后获取该类型对应的申请也就不是问题了。

2、获取标签字段

我们已经有了需要显示的申请,接下来的问题就是如何将申请的数据显示出来,但是在显示数据之前,我们首先需要清楚对应申请类型的哪些字段的数据是需要显示的,这个我们让用户在创建申请类型的时候自主选择哪些类型需要显示,并将其存入field数据表即可(本文的申请类型都是预先定义的)。
知道哪些字段需要显示之后我们还需要把这些字段给记录下来。我们可以定义一个{name: string; key: string; weight: number;}[]类型的变量labels保存,name为表头,key为表头对应关键字,weight用来规定表头项顺序。
然后就是如何获取这个变量值了,在此我们可以通过申请类型来获取到相关的数据。申请类型对应的每个filed(表单项)中记录了构建{name: string; key: string; weight: number;}对象的所需属性,将需要显示的field保存到labels即可。实现如下:

 getLabelsByApplyType(applyType: ApplyType): TableLabel[] {
    const labels: TableLabel[] = [];
    applyType.fields.forEach(field => {
      if (field.isShow) {
        labels.push({name: field.label, key: field.key, weight: field.weight} as TableLabel);
      }
    })
    return labels.sort((a, b) => a.weight - b.weight);
  }

3、获取列表内容

现在我们已经知道了需要显示什么,下面就是获取显示的内容了,先准备一个存放数据的容器contents: Record<string, string>[]。通过之前获取的所有申请,将每个申请转换为一个对象(对应index界面的一个列表项)。转换过程也并不复杂,只要将每个每个apply(申请)对应的filedRecord保存到对象就可以了,不需要显示的也可以不保存,但为了后续功能的实现,id字段是必须要保存的。实现如下:

getContentsByApplys(applys: Apply[]): Record<string, string>[] {
    const contents = [] as Record<string, string>[];
    applys.forEach(apply => {
      const fields = apply.applyType.fields;
      const fieldRecords = apply.fieldRecords;
      const content = {} as Record<string, string>;
      fieldRecords.forEach((fieldRecord, index) => {
        if (index === 0) {
          content['id'] = fieldRecord.apply.id.toString();
        }
        fields.forEach(field => {
          if (field.isShow && fieldRecord.field.id === field.id) {
            content[field.key] = (DynamicValueServices[field.fieldType.type] as DynamicServiceInterface)
              .getValue(fieldRecord, field.dataSet);
          }
        })
      })
      contents.push(content);
    })
    return contents;
  }

4、显示index界面

基础工作已经完成,显示index实现如下:

<table class="table table-striped mt-2" *ngIf="applyTypeId.valid && applyTypeId.value !== ''">
  <thead>
    <tr class="table-primary">
      <th *ngIf="labels.length > 0">序号</th>
      <th *ngFor="let label of labels">{{ label.name }}</th>
      <th *ngIf="labels.length > 0">操作</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let content of contents; index as i">
      <td>{{ i + 1 }}</td>
      <td *ngFor="let label of labels">{{ content[label.key] }}</td>
      <td>
        <a routerLink="edit/{{content['id']}}" class="btn btn-sm btn-primary mr-1"><i class="fas fa-edit"></i>编辑</a>
      </td>
    </tr>
  </tbody>
</table>

内容对象都用到了如下类型:

type Record<K extends keyof any, T> = {
    [P in K]: T;
};

二、新增与编辑实现

这个功能实现起来相对来说比较简单,下面简单介绍一下。

1、获取表单值

无论是新增还是编辑,他们都是用的AppFormComponent这个表单组件,不同的是传入的forms,新增是没有初始值的,而编辑是有初始值的。但是有没有初始值不影响获取值,所以在用户操作修改表单之后获取到对应的值这一步的实现是相同的。

在AppFormComponent组件中添加一个Output属性,当提交表单时,调用方法弹射formGroup.value的值给父组件即可。

@Output() formGroupValue = new EventEmitter<Record<string, string>>();

onSubmit(): void {
    this.formGroupValue.emit(this.formGroup.value);
}
<app-form (formGroupValue)="catchValue($event)" [formInfos]="forms"></app-form>

2、处理表单值

2.1、新增

首先通过申请类型新建一个申请,然后将表单值的每个元素保存到一个FieldRecord类型即可。最后将数据保存即可。实现如下:

catchValue($event: Record<string, string>): void {
    this.dynamicFormService.addApply($event);
}
addApply(formGroupValue: Record<string, string>): void {
    const apply = {
      id: this.applyId++,
      status: 0,
      applyType: this.applyType
    } as Apply;
    const fieldRecords = [] as FieldRecord[];
    this.applyType?.fields.forEach(field => {
      fieldRecords.push(this.getFieldRecord(apply, field, formGroupValue[field.key]));
    })
    apply.fieldRecords = fieldRecords;
    this.applys.push(apply);
}
2.2、编辑

编辑实现更为简单,只需要将FieldRecord的值进行修改即可。实现如下:

catchValue($event: Record<string, string>): void {
  this.dynamicFormService.updateApply(this.editingApplyId, $event);
}
updateApply(applyId: number, formGroupValue: Record<string, string>): void {
  const apply = this.getApplyById(applyId);
  apply.fieldRecords.forEach(fieldRecord => {
    let field = fieldRecord.field;
    fieldRecord.value = formGroupValue[field.key];
  })
}

三、总结

本篇文章通过模拟数据库来实现的列表显示与新增,用服务充当数据库,然后根据ER图来对数据进行转换。在实现上相较于真实情况会简单许多,只希望能够通过本文传达给读者实现思路。

github code


多走几步
124 声望13 粉丝