前言
之前曾经介绍过:
- 动静表单的构建
- 动静表单的数据库的构造
- 如何将数据库中的数据渲染出表单
具体能够参考之前两篇文章(见文末),本文内容如下:
- 显示动静表单的首页列表
- 数据的增改
成果演示:
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图:
一、首页列表显示
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