文章目录
  1. 1. 自定义表单作为表单控件
    1. 1.1. option-dialog
    2. 1.2. hidden-when-dialog
  2. 2. 配置表单
    1. 2.1. 表单的选项和结构
    2. 2.2. 配置表单的配置
  3. 3. 配置生成表单
    1. 3.1. 生成json展示
    2. 3.2. 读取配置
  4. 4. 结束语

因为项目原因又玩上了Angular2(v4.0+),《玩转Angular2》系列用于探索一些灵活或者新的用法。
本文讲述我们使用表单生成的功能,来进行表单的自定义配置和生成。

自定义表单作为表单控件


option-dialog

上一节《玩转Angular2(11)–使用动态表单制作选项配置对话框》我们讲了使用动态表单制作选项配置对话框,这里面我们需要将这样的组件重新作为表单的一部分,在我们的自定义表单中使用。
我们需要调整来使得以下方式可用:

1
<option-dialog [type]="optionsType(control.type)" [formControlName]="control.key"></option-dialog>

这时候我们又需要前面的自定义input神奇了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import {customInputAccessor} from '../../class/custom-input.class';
@Component({
// ...
providers: [customInputAccessor(OptionDialogComponent)]
})
export class OptionDialogComponent implements OnInit {
@Input() type: string = '';
optionsForm: IOption[] = [];
optionControl: ICustomControl[];
isShown: boolean = false;
private model: IOptions[] = []; // 控件的值
private onChange: (_: any) => void;
private onTouched: () => void;
// ...其他省略
ngOnInit()
isValid()
setOptions()
addControl(
saveOptions() {
// 将valid过滤掉
this.model = [].concat(this.optionsForm.map((op: IOption) => {
const {id, text} = op;
return {id, text};
}));
this.isShown = false;
this.onChange(this.model); // 触发值更新
}
// 其他照旧
onBlur() {
this.onTouched();
}
writeValue(value: IOptions[]): void {
if (value && value.length) {
this.model = value;
}
}
registerOnChange(fn: (_: any) => {}): void {
this.onChange = fn;
}
registerOnTouched(fn: () => {}): void {
this.onTouched = fn;
}
}

hidden-when-dialog

我们的配置选项中,有一个是hiddenWhen参数,该参数也是通过配置多项条件的方式,这里我们也添加一个hidden-when-dialog来配置。
由于篇幅关系,这里只看看配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
export const validationsFormControl: ICustomControl[] = [
{
type: 'text',
label: 'key',
key: 'key',
validations: [{
type: 'required',
message: 'key必填'
}]
}, {
type: 'select',
label: 'validate',
key: 'validate',
validations: [{
type: 'required',
message: 'validate必选'
}],
options: [
{id: '>', text: '>'},
{id: '>=', text: '>='},
{id: '<', text: '<'},
{id: '<=', text: '<='},
{id: '==', text: '=='},
{id: '===', text: '==='},
{id: '!=', text: '!='},
{id: '!==', text: '!=='},
{id: 'indexOf', text: 'indexOf'},
]
}, {
type: 'text',
label: 'param',
key: 'param',
validations: [{
type: 'required',
message: 'param必填'
}]
}
];

至于组件的html和js大家可以翻阅项目代码(在文章最后有链接)。

配置表单


表单的选项和结构

目前为止,我们的表单控件格式大致是下面这些:

1
2
3
4
5
6
7
8
9
10
11
export interface ICustomControl {
type: string;
label: string;
key: string;
validations?: IValidations[];
options?: IOptions[];
limit?: ILimit;
hiddenWhen?: IHiddenCondition;
description?: string;
setOptions?: boolean;
}

我们需要添加一些选项或者是输入,来让使用的人生成以上的格式,然后生成想要的表单。
这里面需要详细配置的有:

  • validations
    • 校验方式,如requiredmaxLength
    • 这里可以使用checkbox-with-input的方式来配置
1
2
3
4
5
export interface IValidations {
type: string;
param?: any;
message: string;
}
  • options
    • 选项配置,当选择的类型type需要配置选项时出现
    • type == 'select' || type == 'radio' || type == 'checkbox'等等
    • 前面的option-dialog已封装该功能,并提供自定义表单使用,但目前只支持idtext的配置
1
2
3
4
5
6
export interface IOptions {
id: string;
text: string;
withInput?: boolean;
type?: 'text' | 'number' | 'email';
}
  • limit
    • 图片校验,只当选择图片上传时使用
    • 使用checkbox-with-input的方式来配置
1
2
3
4
5
6
export interface ILimit {
width?: number;
height?: number;
size?: number;
type?: string;
}
  • hiddenWhen
    • 条件符合时隐藏,可配置多个条件,并设置多个条件的关系(||&&
    • hidden-when-dialog
1
2
3
4
5
6
7
8
9
export interface IHiddenCondition {
condition: '||' | '&&' | '';
validations?: IHiddenValidate[];
}
export interface IHiddenValidate {
key: string;
validate: string;
param: string | number;
}

配置表单的配置

接下来我们来配置生成这样一个用来生成配置的表单吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
export const normalFormControl: ICustomControl[] = [
{
type: 'select',
label: '类型',
key: 'type',
validations: [{
type: 'required',
message: '类型必选'
}],
options: [
{id: 'text', text: 'input-text'},
{id: 'number', text: 'input-数字'},
{id: 'select', text: 'select'},
{id: 'radio', text: 'radio组'},
{id: 'checkbox', text: 'checkbox组'},
{id: 'date', text: '日期'},
{id: 'date-time', text: '日期+时间(分钟)'},
{id: 'date-hour', text: '日期+时间(小时)'},
{id: 'upload-image', text: '图片上传'},
]
}, {
type: 'text',
label: '字段key',
key: 'key',
validations: [{
type: 'required',
message: '字段key必填'
}, {
type: 'maxLength',
param: 15,
message: '字段key最多15个字符'
}, {
type: 'minLength',
param: 3,
message: '字段key最少3个字符'
}]
}, {
type: 'text',
label: '标签说明',
key: 'label',
validations: [{
type: 'required',
message: '标签说明必填'
}]
}, {
type: 'checkbox-with-input',
label: '校验',
key: 'validations',
options: [
{id: 'required', text: '必填/必选'},
{id: 'email', text: '邮件格式'},
{id: 'maxLength', text: '最大字符数', withInput: true, type: 'number'},
{id: 'minLength', text: '最小字符数', withInput: true, type: 'number'}
]
}, {
type: 'checkbox-with-input',
label: '图片上传限制',
key: 'limit',
options: [
{id: 'width', text: '宽(px)', withInput: true, type: 'number'},
{id: 'height', text: '高(px)', withInput: true, type: 'number'},
{id: 'size', text: '大小', withInput: true, type: 'number'},
{id: 'type', text: '类型', withInput: true, type: 'text'}
],
description: '类型限制可填 jpg | png | gif',
hiddenWhen: { // 当选择上传图片组件才显示
condition: '||',
validations: [{
key: 'type',
validate: '!=',
param: 'upload-image'
}]
}
}, {
type: 'select',
label: '选项配置',
key: 'options',
options: [],
hiddenWhen: { // 选择带选项组件显示
condition: '&&',
validations: [{
key: 'type',
validate: '!=',
param: 'select'
}, {
key: 'type',
validate: '!=',
param: 'checkbox'
}, {
key: 'type',
validate: '!=',
param: 'radio'
}]
},
setOptions: true
}, {
type: 'text',
label: '描述说明',
key: 'description'
}, {
type: 'radio', // 带选项的可配置提供自定义选项
label: '是否自定义选项',
key: 'setOptions',
options: [
{id: '', text: '否'},
{id: 'withOption', text: '是'},
],
hiddenWhen: {
condition: '&&',
validations: [{
key: 'type',
validate: '!=',
param: 'select'
}, {
key: 'type',
validate: '!=',
param: 'checkbox'
}, {
key: 'type',
validate: '!=',
param: 'radio'
}]
}
}, {
type: 'radio', // 该字段用来判断是否启用下面配置,不作为最终生成的配置选项
label: '是否启用条件隐藏',
key: 'withValidation',
options: [
{id: '0', text: '否'},
{id: '1', text: '是'},
]
}, {
type: 'hidden-when-dialog',
label: '隐藏条件配置',
key: 'hiddenWhen',
hiddenWhen: {
condition: '&&',
validations: [{
key: 'withValidation',
validate: '!=',
param: '1'
}]
}
}
];

大家会发现,上面的配置并不完全可以得到我们最后想要的配置,如limitoptions的格式,故我们最后还需要输出时调整:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
formJson() {
const jsonResult = [];
this.customForms.forEach(form => {
// 取出需要使用的字段
const {type, label, key, limit, validations, options, description, setOptions, withValidation, hiddenWhen} = form;
const validationArr = [];
const limitObj = {};
if (validations) {
Object.keys(validations).forEach(valid => {
const value = validations[valid].value;
let message = valid;
if (value != null) {
message += `: ${value}`;
}
if (validations[valid].checked) {
validationArr.push({
type: valid,
param: value == null ? undefined : Number(value),
message
});
}
});
}
if (limit) {
Object.keys(limit).forEach(con => {
if (limit[con].checked) {
limitObj[con] = limit[con].value;
}
});
}
if (withValidation != '1') {
hiddenWhen = undefined;
}
jsonResult.push({
limit: limitObj,
validations: validationArr,
type, label, key, options, description, setOptions, hiddenWhen
});
});
this.json = JSON.stringify({jsonResult});
}

而我们的html模版长这样:

1
2
3
4
5
6
7
8
9
<div style="margin-top: 30px;">
<h1>自定义表单配置</h1><a class="btn btn-info" (click)="addControl()" >添加表单选项</a>
<div *ngFor="let form of customForms; let i = index;">
<h2>表单控件{{i + 1}}</h2>
<dynamic-form [config]="customControl" [model]="form"></dynamic-form>
</div>
<h1><a class="btn btn-success" (click)="formJson()" >生成JSON</a></h1>
<json-to-html [hidden]="!json" [(ngModel)]="json"></json-to-html>
</div>

配置生成表单


生成json展示

上面大家也看到了json-to-html,其实主要拿以前实现的其中一种方法来生成带样式的json,具体实现大家可以参考《将json输出为html(一):字符串正则匹配》《将json输出为html(二):js数据类型判断实现》

这里我们将它稍微封装一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@Component({
selector: 'json-to-html',
template: `
<div></div>`,
providers: [customInputAccessor(JsonToHtmlComponent)]
})
export class JsonToHtmlComponent implements AfterViewInit {
@Input() options: object = {};
@Input() isEdit: boolean = false;
@Output() change = new EventEmitter();
private editor: any;
private el;
private model: any = []; // 控件的值
private onChange: (_: any) => void;
private onTouched: () => void;
constructor(el: ElementRef) {
this.el = el;
}
ngAfterViewInit() {
this.setValue(this.model);
}
setValue(value: any){
if(value){
this.el.nativeElement.innerHTML = JsonToHtml(value);
}
}
writeValue(value: string): void {
if (value) {
this.model = value;
this.setValue(value);
}
}
// 其他照旧
}

读取配置

我们将上面生成的json复制一下,粘贴到一个地方,然后就可以生成想要的表单了。
这里配置读取很简单:

1
2
3
4
5
6
7
8
9
10
11
<div style="margin-top: 30px;">
<h1>自定义表单生成</h1>
<div>
<h2>输入json</h2>
<textarea [(ngModel)]="json"></textarea>
<h2><a class="btn btn-success" (click)="buildForm()" >生成表单</a></h2>
</div>
<div>
<dynamic-form *ngIf="isShown" [config]="customControl"></dynamic-form>
</div>
</div>

使用*ngIf主要是方便控制[config]的输入更改的时候重新生成,因为自定义表单通常不会动态改变,故我们不将该功能拓展进去。
生成配置也很简单:

1
2
3
4
5
6
7
8
9
10
customControl: ICustomControl[];
json: any;
isShown: boolean = false;
buildForm(){
this.isShown = false;
const config = JSON.parse(this.json);
this.customControl = config.jsonResult;
this.isShown = true;
}

这样,我们就能实现表单的创建和生成啦。效果图如下:

image

image

image

image

image

结束语


项目中表单配置部分大致的实现也是差不多告一段落啦,后面可能更多的是补充和优化而已。
而其实项目还可以多种拓展呢,像这样的添加配置像(validations和hiddenWhen)也可以通过抽象做成更加通用的功能。
此处查看项目代码
此处查看页面效果

完整项目也可查看angular-custom-app项目

查看Github有更多内容噢:https://github.com/godbasin
更欢迎来被删的前端游乐场边撸猫边学前端噢

码生艰难,写文不易,给我家猪囤点猫粮了喵~

作者:被删

出处:https://godbasin.github.io

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

文章目录
  1. 1. 自定义表单作为表单控件
    1. 1.1. option-dialog
    2. 1.2. hidden-when-dialog
  2. 2. 配置表单
    1. 2.1. 表单的选项和结构
    2. 2.2. 配置表单的配置
  3. 3. 配置生成表单
    1. 3.1. 生成json展示
    2. 3.2. 读取配置
  4. 4. 结束语