vForm custom component description

Keywords: Vue.js

Custom components

1, Custom component description

Custom components include vue files and configuration files

Configuration file format:

[
	{
		type:"",					The name of the component definition is the component vue file export default{ name:"" } Name of the definition
		icon:"",					Show in widget-panel Component icon, which can be iconfont Icons or other icons
		plugin:true,				The tag is a custom component. This is true That's all
		formItemFlag:true,			Mark display fromItem
		options:{					Custom component properties, if yes size,disabled And other predefined attributes, you can automatically display the attribute configuration. If it is other attributes, you need to setting Set property configuration item in
			
		},
		setting:{					Show in setting-panel Property configuration item for
			commonSetting:[],		Common properties
			advancedSetting:[],		Advanced properties
			eventSetting:[]			Event properties
		},
		i18n:{						International resources
			zh-CN:{},
			en-US:{}
		}
	}
]

Description: the component name of widget panel displays i18n.zh-CN.designer.widgetLabel. {component name}
For example:

i18n:{
	zh-CN:{
		"designer": {
			"widgetLabel": {
				"deptSelect": "Department selection",
			}
		}
	}
}

commonSetting and advancedSetting:

{
	name: 'isMultiSelect',									The attribute name of the custom component, that is, the name of the component props
	displayName: "setting.multiSelect",						Display name during property configuration, supporting internationalization
	tooltip: "setting.multiSelect",							Property configuration tooltip,If it is not configured, it will not be displayed. Internationalization is supported
	field: {
		type: "RadioGroup",						`Property configuration component`Types of, including: Input,Select,Radiobox,Check,Checkbox,Grid, Prefix omitted Setting,See the modified document for details setting-pannel/controls part
		props: {								`Property configuration component`Properties, using iview Corresponding component attribute definition
			size:'small'						
		},
		child: [{								`Property configuration component`Sub item, each component is different
				value: "choice",
				label: "Left",
			},
			{
				value: "GET",
				label: "in",
			},
			{
				value: "POST",
				label: "right",
			}
		]
	}
}

eventSetting:

{
	eventName: 'onselect',						Name of custom event
	eventParam: 'arg,arg1',						For the parameters of custom events, you can directly use the names defined here in the event editing dialog box
	tooltip: "setting.muiltiSelect",			setting-panel Description of custom attribute configuration, supporting internationalization
	displayName: "setting.muiltiSelect",		setting-panel The name of the custom attribute configuration, supporting internationalization
}

Component vue file

import i18n_lang from './i18n.js';					Import the internationalization configuration file of the component. I configure the main configuration of the component, setting to configure, i18n The configuration is split
import plugin_mixin from '../plugin_mixin.js'		mixin 
export default{
	name: "projectSelect",							Component name, that is, the name of the configuration file type
	mixins: [plugin_mixin],
	data(){
		return {
			
		}
	},
	props:{
		value:{										Because when using components v-model Bind form field values, so it needs to be split into props of value(Get values) and methods Inside $emit("input"); 
			type:String,
			default:""
		}
	}
	watch:{
		value:function(val){
			if(val!=null){
				this.selectedProject=this.value;
			}else{
				this.selectedProject={};
			}
		}
	},
	created(){
	},
	methods:{
		selected(val){
			this.$emit("input",val);			   trigger v-model,Bind value to form formDataModel In the object.
			this.$emit("customEvent","selected",[this.selectedProject,row._id]);
												   Trigger event properties, selected Is the name of the custom event, followed by array Is a parameter. corresponding json configuration file eventSetting->eventName,eventSetting->eventParam
		}
	}
}

plugin_mixin.js

export default {
	inject:["i18n","formConfig","formDesigner"],									i18n internationalization,formConfig,In order to realize the unified setting of forms size,formDesigner Judge the component usage environment, design mode or rendering mode, and the rendering mode is null
	computed:{
		widgetSize(){																Get the uniform settings of the form size
			//If it is the preview interface of the designer
			if(!!this.formDesigner){
				return this.size||this.formDesigner.formConfig.size||undefined;
			}else{
				return this.size||this.formConfig().size||undefined;				
			}
		}		
	},
	  methods: {
		  i18nt(key){
			return this.i18n.methods.i18nt(key);
		  },
		  setup_i18n(i18nlang){			  											This seems useless
			  for(let key in i18nlang){
			  	this.i18n.methods.appendResource(key,i18nlang[key]);
			  }
		  }
	  }
}

explain:
I always feel that the implementation of inject is not elegant... But it is troublesome to use i18n and formConfig in components. I didn't think of any other way.





2, Mechanism description

Show custom component Icon

1. Load the json configuration, write it to props - > customfields of widget panel / index.vue, and display the component name and icon according to the json configuration.

components-iview/form-designer/index.vue
Line: 88 add Props customFields
First write the json configuration content of the component into the customFields of vform designer, and then write it into the customFields of widget pannel and setting pannel respectively

customFields: {
	type: Array,
	default: () => {}
}

widget-pannel/index.vue
Line 51 shows the custom widget

<Panel name="4" :title="i18nt('designer.customFieldTitle')" v-if="customFields.length>0">
	{{i18nt('designer.customFieldTitle')}}
	<div slot="content">
		<draggable tag="ul" :list="customFields" :group="{name: 'dragGroup', pull: 'clone', put: false}"
			:clone="handleFieldWidgetClone" ghost-class="ghost" :sort="false">
			<li v-for="(fld, index) in customFields" :key="index" class="field-widget-item"
				:title="fld.displayName" @dblclick="addFieldByDbClick(fld)">
				<span class="iconfont" :class="[fld.icon]">
					{{i18nt(`designer.widgetLabel.${fld.type}`)}}
				</span>
			</li>
		</draggable>
	</div>
</Panel>

Line 106 props add customFields

customFields:{
	type:Array,
	default:()=> []
}



Display property configuration items

1. Six attribute configuration components are defined.

setting-pannel/controls
Six attribute configuration components are defined
SettingInput: used to edit string and numeric attributes
SettingSelect: used to edit drop-down properties
SettingCheckbox: used to edit the true/false attribute
SettingRadioGroup: used to edit radio attribute groups, such as field label alignment of common attributes
SettingCheckGroup: used to edit the checkgroup group. It doesn't seem to be very useful. It's changed according to the RadioGroup
SettingGrid: used to edit multiple options, such as "option settings" of checkbox multiple options

2. Load the json configuration, write it to props - > customfields of setting pannel / index.vue, and display the component name and icon according to the json configuration

setting-pannel/index.vue
Line: 1066 props add customFields
Get the configuration of custom components

customFields: Array
3. Parse the common attribute / advanced attribute / event attribute configuration in json, and dynamically load the attribute configuration component

setting-pannel/index.vue
Line 367: add the dynamic configuration of common attributes, and display the corresponding configuration item parts according to the configuration file

<template v-if="designer.selectedWidget.plugin">
	<FormItem  v-for="setting in designer.selectedWidget.setting.commonSetting">
		<span slot="label">
			{{i18nt(setting.displayName)}}
			<Tooltip v-if="setting.tooltip" :content="i18nt(setting.tooltip)">
				<i class="ivu-icon ivu-icon-md-information-circle"></i>
			</Tooltip>
		</span>
		<component :is="'Setting'+setting.field.type" :default="setting.default" :field="setting.field" v-model="optionModel[setting.name]"></component>
	</FormItem>
</template>

Line: 582 add dynamic configuration of advanced components

<template v-if="designer.selectedWidget.plugin">
	<FormItem  v-for="setting in designer.selectedWidget.setting.advancedSetting">
		<span slot="label">
			{{i18nt(setting.displayName)}}
			<Tooltip v-if="setting.tooltip" :content="i18nt(setting.tooltip)">
				<i class="ivu-icon ivu-icon-md-information-circle"></i>
			</Tooltip>
		</span>
		<component :is="'Setting'+setting.field.type" :default="setting.default" :field="setting.field" v-model="optionModel[setting.name]"></component>
	</FormItem>
</template>

Line: 684 add dynamic configuration of events

<template v-if="designer.selectedWidget.plugin">
	<FormItem  v-for="setting in designer.selectedWidget.setting.eventSetting">
		<span slot="label">
			{{i18nt(setting.displayName)}}
			<Tooltip v-if="setting.tooltip" :content="i18nt(setting.tooltip)">
				<i class="ivu-icon ivu-icon-md-information-circle"></i>
			</Tooltip>
		</span>
		<Button type="info" icon="md-create" plain round @click="editEventHandler(setting.eventName)">
			{{i18nt('designer.setting.addEventHandler')}}
		</Button>
	</FormItem>
</template>
4. Handle custom events

setting-pannel/index.vue
Line: 1412 data add eventPluginParamsMap empty object

eventPluginParamsMap:{
					
},

Line 980: prompt in the edit event dialog box. If the event name is not found in eventParamsMap, load the displayed name and parameters from eventPluginParamsMap

<div class="codeEditTip">{{(optionModel?optionModel.name:'') + '.' + (eventParamsMap[curEventName]||eventPluginParamsMap[curEventName])}}</div>

Line: the 1500 watch section modifies the selectedWidget
If a custom component is selected, the spelling: method name (parameter) string is written to the eventPluginParamsMap object and provided to the edit event dialog box

'designer.selectedWidget': {
	handler(val) {
		if (!!val) {
			this.activeTab = "1"
			
			if(val.plugin){
				for(let i in val.setting.eventSetting){
					var event=val.setting.eventSetting[i];
					this.eventPluginParamsMap[event.eventName]=`${event.eventName}(${event.eventParam}) {`;
				}
			}
		}
	}
},



Custom component display mechanism

1. Use Vue.component to register custom components in advance

main-iview.js
import SettingControls from '@/components-iview/form-designer/setting-panel/controls/';
Vue.use(SettingControls);

2. Dynamically display and call the custom component, inject the options attribute of the custom component into the fieldModel of the two-way data binding form, and implement the custom event handler handlePluginEvent

form-widget/field-index.vue
form-widget/field-widget.vue
Line 321: add dynamic component

<template v-if="!!field.plugin">
	<components :is="field.type" v-bind="field.options" v-on="field.event" v-model="fieldModel" @customEvent="handlePluginEvent"></components>
</template>



Custom component handling events

1. All custom events of the component trigger customEvent

Component.vue file
Triggered where business needs

this.$emit("customEvent","Event property name",[Parameter 1, parameter 2...]);
2. Event program dynamically set with handlePluginEvent

form-widget/field-index.vue
Line 799: add personalized event processing

handlePluginEvent(eventName,eventArgs) {
	let param=[];
	for(let i in this.field.setting.eventSetting){
		let event=this.field.setting.eventSetting[i];
		if(event.eventName===eventName){
			param=event.eventParam.split(",");
		}
	}
	if (!!this.field.options[eventName]) {
		let customFunc = new Function(...param,this.field.options[eventName])
		customFunc.apply(this,eventArgs)
	}
},		



International solutions

1. designer

components-iview/form-designer/index.vue
Line: 143 methods add the internationalization configuration for resolving custom components, and call for execution when mounted

initPluginI18n(){
	for(let i in this.customFields){
		let plugins=this.customFields[i];
		if(plugins.i18n){
			for(let lang in plugins.i18n){
				i18n.methods.appendResource(lang,plugins.i18n[lang]);
			}
		}
	}
},
2. Renderer

form-render/index.vue
Line 61 props add customFields
Getting the configuration of custom components mainly requires international configuration

customFields: {
	type: Array,
	default: () => {}
}

Line: 123 watch adds the internationalization configuration for resolving custom components

customFields:{
	deep: true,
	handler(val, oldVal) {
		val.forEach(plugin=>{
			if(plugin.i18n){
				for(let key in plugin.i18n){
					i18n.methods.appendResource(key,plugin.i18n[key]);
				}
			}
		})
	}
}
3. Customize the internal internationalization of components, such as buttons, pop-up window titles and prompt text

components-iview/form-designer/index.vue
Line 108: add Provide i18n and provide the methods of i18n to the plug-in

provide() {
	return {
		i18n:i18n
	}
},

form-render/index.vue
Line 76 provide s i18n, which is provided to the component

i18n:i18n

Component interior
Get the instance of i18n through inject, and then use the methods.i18nt method



Custom component response form global configuration

1. Inject formDesigner and formConfig to determine whether it is design mode or rendering mode through formDesigner

form-widget/field-index.vue
Line: 63 Provide add formDesigner

form-render/index.vue
Line 71 provide adds formDesigner and injects null

formDesigner:null,	//Inject null so that the plug-in can distinguish whether it is called in designer mode or Render mode
2. Use plugin when developing custom components_ mixin.js



Other modified problems

Do you see if this is reasonable?

1. The dynamically obtained form configuration does not take effect

When using VFormRender, if formJson is written directly in the data, the form can be displayed, but if it is read from the database and modified the formData, the form cannot be displayed.

form-render/index.vue
Originally, formConfig was written in calculated and then provide d to the sub components. I moved it to methods, but in this way, the value was changed into a function reference

export default{
	...
	provide(){
		return {
			...
			formConfig:this.formConfig
			...
		}
	},
	computed:{
		//formConfig(){
		//	return this.formJson.formConfig
		//}
		...
	},
	methods:{
		formConfig(){
			return this.formJson.formConfig
		}
		...
	}
}

Where the sub component uses this.config, it is changed to this.config()
It involves labelWidth, labelAlign and size in field-widget.vue computed



2. In the editing function, the modified content cannot be obtained when reading the database, setting the form field content (setFormData), and then obtaining (getFormData).

form-render/index.vue
The formDataModel was re assigned this_ provided.globalModel.formModel

getFormData(needValidation = true) {
	//Prevent editing mode from getting the latest data
	this.formDataModel=this._provided.globalModel.formModel;
	...
}



3. Form emptying function

Cannot empty
form-render/index.vue

resetForm() { //Reset Form 
	...
	// this.$refs.renderForm.clearValidate() 	 customary
	this.$refs.renderForm.resetFields();		Change to
}

Posted by smellicus on Fri, 08 Oct 2021 12:13:19 -0700