动态表单方案vue-form-generator调研

3,260 阅读1分钟

vue-form-generator简介

JSON 动态化表单开发方案,点击查看源码github。下文将详细拆解vue-form-generator的实现逻辑。

JSON Schema结构

{
	fields: [
		{
			type: "input",
			inputType: "text",
			label: "First Name",
			model: "first_name",
			attributes: {
				input: {
					"data-toggle": "tooltip"
				},
				wrapper: {
					"data-target": "input"
				}
			}
		},
		{
			type: "submit",
			buttonText: "Change Previous Type",
			attributes: {
				input: {
					"data-target": "toggle"
				}
			},
			onSubmit: () => {
				// this.schema.fields[2].type = "input";
				if (this.schema.fields[2].inputType === "color") {
					this.schema.fields[2].inputType = "text";
				} else {
					this.schema.fields[2].inputType = "color";
				}
			}
		}
	]
}

动态表单的实现逻辑

第一步:引入动态表单组件

<vue-form-generator :schema="schema" :model="model" :options="formOptions" ref="form" :is-new-model="isNewModel" @model-updated="modelUpdated" @validated="onValidated"></vue-form-generator>

第二步:vue-form-generator组件源码

<template lang="pug">
div.vue-form-generator(v-if='schema != null')
	fieldset(v-if="schema.fields", :is='tag')
		template(v-for='field in fields')
			form-group(v-if='fieldVisible(field)', :vfg="vfg", :field="field", :errors="errors", :model="model", :options="options", @validated="onFieldValidated", @model-updated="onModelUpdated")

	//- 表单组
	template(v-for='group in groups')
		fieldset(:is='tag', :class='getFieldRowClasses(group)')
			legend(v-if='group.legend') {{ group.legend }}
			template(v-for='field in group.fields')
				form-group(v-if='fieldVisible(field)', :vfg="vfg", :field="field", :errors="errors", :model="model", :options="options", @validated="onFieldValidated", @model-updated="onModelUpdated")
</template>

component is 实现表单元素组件动态渲染

<template>
	<div class="form-group" :class="getFieldRowClasses(field)">
		<label v-if="fieldTypeHasLabel(field)" :for="getFieldID(field)" :class="field.labelClasses">
			<span v-html="field.label"></span>
			<span v-if='field.help' class="help">
				<i class="icon"></i>
				<div class="helpText" v-html='field.help'></div>
			</span>
		</label>

		<div class="field-wrap">
			<!-- component is实现表单元素组件动态渲染 -->
			<component ref="child" :is="getFieldType(field)" :vfg="vfg" :disabled="fieldDisabled(field)" :model="model" :schema="field" :formOptions="options" @model-updated="onModelUpdated" @validated="onFieldValidated"></component>
			<div v-if="buttonVisibility(field)" class="buttons">
				<button v-for="(btn, index) in field.buttons" @click="buttonClickHandler(btn, field, $event)" :class="btn.classes" :key="index" v-text="btn.label" :type="getButtonType(btn)"></button>
			</div>
		</div>

		<div v-if="field.hint" class="hint" v-html="fieldHint(field)"></div>

		<div v-if="fieldErrors(field).length > 0" class="errors help-block">
			<span v-for="(error, index) in fieldErrors(field)" :key="index" v-html="error"></span>
		</div>
	</div>
</template>

第四步:表单元素组件实现(例:input)

<template lang="pug">
.wrapper(v-attributes="'wrapper'")
	input.form-control(
		:id="getFieldID(schema)",
		:type="inputType",
		:value="value",
		@input="onInput",
		@blur="onBlur",
		:class="schema.fieldClasses",
		@change="schema.onChange || null",
		:disabled="disabled",
		:accept="schema.accept",
		:alt="schema.alt",
		:autocomplete="schema.autocomplete",
		:checked="schema.checked",
		:dirname="schema.dirname",
		:formaction="schema.formaction",
		:formenctype="schema.formenctype",
		:formmethod="schema.formmethod",
		:formnovalidate="schema.formnovalidate",
		:formtarget="schema.formtarget",
		:height="schema.height",
		:list="schema.list",
		:max="schema.max",
		:maxlength="schema.maxlength",
		:min="schema.min",
		:minlength="schema.minlength",
		:multiple="schema.multiple",
		:name="schema.inputName",
		:pattern="schema.pattern",
		:placeholder="schema.placeholder",
		:readonly="schema.readonly",
		:required="schema.required",
		:size="schema.size",
		:src="schema.src",
		:step="schema.step",
		:width="schema.width",
		:files="schema.files"
		v-attributes="'input'")
	span.helper(v-if="schema.inputType.toLowerCase() === 'color' || schema.inputType.toLowerCase() === 'range'") {{ value }}
</template>