在最近的项目中遇到动态form表单的问题,遇到很多编辑的地方,而且编辑的form表单很多内容,大概几十个字段,而且有不同的类型,有文本框,也有下拉列表,有时间控件,还有数值控件等等,如果采用硬编码的方式会有很多问题,代码量太大,容易漏数据,很容易出错;于是正好找到Angular有类似的例子,https://angular.io/guide/dynamic-form,参考这里,实现动态form组件;
1>定义Form的组件基础类
这里value是泛型,基本都是string类型吧;key是Form中每一个控件的名称,对应提交时数据的属性值;label是表单字段显示的label;
required是字段是否是必须填的,order是排序字段,controlType是控件的类型,比如下拉的,文本框,时间控件,数值输入框等;
export class FieldBase<T> {
value: T;
key: string;
label: string;
required: boolean;
order: number;
controlType: string;
constructor(
options: {
value?: T;
key?: string;
label?: string;
required?: boolean;
order?: number;
controlType?: string;
} = {}
) {
this.value = options.value;
this.key = options.key || '';
this.label = options.label || '';
this.required = !!options.required;
this.order = options.order === undefined ? 999 : options.order;
this.controlType = options.controlType || '';
}
}
2>根据需求定义不同类型的具体组件
常用的可能是下拉选择框,文本组件,数值控件等;需要继承上面的FileldBase类,比如下拉选择框
export class DropDownField extends FieldBase<string> {
controlType = DROP_DOWN;
options: [] = [];
constructor(options: {} = {}) {
super(options);
this.options = options['options'] || [];
}
}
指定下拉组件的类型,以及下来选择的属性options,这是下拉选择值的数组;
下面是文本框输入控件,多了个属性placeHolder;
export class TextboxField extends FieldBase<string> {
controlType = TEXT_BOX;
placeHolder: string;
constructor(options: {} = {}) {
super(options);
this.placeHolder = options['placeHolder'] || '';
}
}
其他的类型不一一列举,大致是类似的;
3.Form 组件
这里form组件中添加了submitBtn来控制提交按钮的显示文本,submitForm事件和cancelEvent事件作为提交和取消处理的事件向外传播;
@Component({
selector: 'jhi-dynamic-form',
templateUrl: './dynamic-form.component.html',
styleUrls: ['dynamic-form.css']
})
export class DynamicFormComponent implements OnInit {
@Input() fields: FieldBase<any>[] = [];
@Input() submitBtn: string;
form: FormGroup;
@Output() submitForm: EventEmitter<object> = new EventEmitter();
@Output() cancelEvent: EventEmitter<void> = new EventEmitter();
constructor(private qcs: FieldControlService) {}
ngOnInit() {
this.form = this.qcs.toFormGroup(this.fields);
}
onSubmit() {
this.submitForm.emit(this.form.value);
}
cancel() {
this.cancelEvent.emit();
}
}
初始化的时候,通过FieldControlService 来构造Form对象;具体如下所示,这里针对下拉多选做了另外的处理;通过fields列表构造了FormGroup对象;
export class FieldControlService {
constructor() {}
toFormGroup(fields: FieldBase<any>[]) {
const group: any = {};
fields.forEach(field => {
switch (field.controlType) {
case MULTIPLE_SELECT:
group[field.key] = field.required ? new FormControl(field.value, Validators.required) : new FormControl(field.value);
break;
default:
group[field.key] = field.required
? new FormControl(field.value || null, Validators.required)
: new FormControl(field.value || null);
}
});
return new FormGroup(group);
}
}
对应的dynamic-form.component.html内容如下所示
<div>
<form nz-form (ngSubmit)="onSubmit()" [formGroup]="form">
<div nz-row [nzGutter]="24">
<jhi-dynamic-form-field *ngFor="let field of fields" [field]="field" [form]="form"></jhi-dynamic-form-field>
</div>
<div nz-col style="display: flex;justify-content:space-between;margin-bottom: 20px;">
<button nz-button nzType="primary" type="button" (click)="cancel()">
取消
</button>
<button style="margin-right:40px;" nz-button nzType="primary" type="submit" [disabled]="!form.valid">
{{submitBtn}}
</button>
</div>
</form>
</div>
这里引入了循环field显示即可;针对每一个field做了个封装,封装为jhi-dynamic-form-field组件,通过组件类型来显示不同的组件,具体如下所示
export class DynamicFormFieldComponent {
@Input() field: any;
@Input() form: FormGroup;
}
<div class="ant-advanced-search-form" [formGroup]="form">
<div nz-col [nzSpan]="6">
<nz-form-item nzFlex style="flex-direction: column">
<nz-form-label><span class="label-span">{{field.label}}</span></nz-form-label>
<nz-form-control [nzSpan]="14" [ngSwitch]="field.controlType">
<!--文本输入框-->
<input nz-input *ngSwitchCase="'textbox'" type="text" [formControlName]="field.key" [placeholder]="field.placeHolder">
<!--下拉选择框-->
<div *ngSwitchCase="'dropdown'">
<nz-select nzShowSearch nzAllowClear=true [id]="field.key" *ngIf="!field.isLabelOption"
[formControlName]="field.key">
<nz-option *ngFor="let opt of field.options" [nzLabel]="opt" [nzValue]="opt">{{opt}}
</nz-option>
</nz-select>
</div>
<!--多选下拉框-->
<nz-select nzShowSearch nzAllowClear=true nzMode="multiple" [id]="field.key"
*ngSwitchCase="'multipleSelect'"
[formControlName]="field.key">
<nz-option *ngFor="let opt of field.options" [nzLabel]="opt" [nzValue]="opt">{{opt}}
</nz-option>
</nz-select>
</nz-form-control>
</nz-form-item>
</div>
</div>
之后就可以使用dynamic-form组件了;只需要传入fields数组即可;然后传入提交表单和取消表单的事件;整个动态form就可以成功渲染了,其中用到了阿里的antd的form组件库,需要在module中引入进去;