Demand from product MM

1. Let’s start with a simple form requirement. The following is part of a simple data collection table that appears when you select “Effective Now”

“Effective Date” and required.

It is easy to implement this requirement with Vue + Element UI. Start VSCode for a regular operation and finish work in 10 minutes.

2. The next day, PRODUCT MM came back again, and there was a change in the requirements, as shown below:

When the activity type is punching order or feedback, the effective condition is supported; Conditional support activity number, activity days (the relationship between the two is and)

How to do? Launch VSCode, is a routine operation, this change is a little trouble, an hour to finish work, of course, to release online, is inevitable push, build, test, packaging, redeployment.

3. However, the game was not over, and two days later the product MM came to you again, because the demand missed a place: the effective conditions need to add “peak” judgment, as shown in the figure below:

And so on and so forth, with requirements constantly iterating, front-end development constantly running, until one day you end up looking like this:

The solution

In view of the above very changeable form requirements, a brief analysis shows that the changes are mainly form control and logical judgment, so the first thing I think of is to develop a configuration form, design a new form Schema specification, and then write form JSON object according to schema, and finally dynamically generate form from form JSON. Here is a common JSON code for a Schema-based form:

{the title: 'the activity type, the key:' act_type, type: 'radio' props: {options: {1: 'new', 2: 'rush order, 3:' feedback '}}},Copy the code

This approach is very mature, and many mature open source forms projects have adopted this approach, but this approach has two obvious disadvantages:

1. Users need to learn form Schema specifications;

2. Difficulty in implementing complex form interaction logic.

To solve the cost of learning the first schema specification, you can develop a drag-and-drop wysi-WYG online form designer based on the form Schema. There are many open source projects that implement this, such as various form generators, form Creators, etc. I also built a VForm. Interested in children’s shoes can try:

VForm, a Vue dynamic form designer, click here to experience

In order to solve the second problem, how to realize the complex interactive logic of dynamic forms is also the main goal of this paper. The solution of this paper is to add a programmable interface to dynamic forms, that is, through the interaction events of components and API methods to realize the interactive logic, and JS code is the king.

Implement a programmable interface to the form

From the first part of the form requirements, to implement the form interaction logic, the first step is to expose the component’s interaction events, such as the onChange event triggered when the “Activity Type” component clicks on the change; The second step is to perform precise manipulation of components in events, such as showing or hiding certain components, setting component mandatory properties, setting component disabled states, adding or removing component CSS styles, and so on.

The first step is as simple as adding a component’s custom event attribute to the form schema. The following schema adds seven custom events to the input component:

{type: 'input', icon: 'text-field', formItemFlag: true, options: {name: ", // component base attribute label: ", labelAlign: ", type: 'text', defaultValue: "', placeholder:" ', / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- onCreated: "', / / custom event onMounted: ' 'the onInput: '', onChange: '', onFocus: '', onBlur: '', onValidate: '', }, },Copy the code

The next step is to add a code editor component that supports syntax highlighting and code hints. Select the mature and proven AceEditor here. GitHub has ace- Builds packed with builds that are easy to install and use:

Install ACE: NPM I ACE – Builds

Then encapsulate a simple JS code editor based on ACE, and intercept part of the code as follows:

<template> <div class="ace-container"> <! -- Use id in official documents, which is forbidden here, and may cause problems after later packaging. <div class="ace- Editor "ref=" ACE "></div> </div> </template> <script> import ACE from 'ACE -builds' /* When this line is enabled, webpack generates many dynamically loaded JS files, which are not easy to deploy. Special tip: After this line is disabled, you need to call ace.config.set('basePath', 'path... ') specify dynamic JS load URL!! */ //import 'ace-builds/webpack-resolver' import 'ACE -builds/src-min-noconflict/theme-sqlserver 'ace-builds/src-min-noconflict/ modes-json '// import 'ace-builds/src-min-noconflict/ modes-json' // import 'ace-builds/src-min-noconflict/mode-css' // import 'ace-builds/src-min-noconflict/ext-language_tools' import {ACE_BASE_PATH} from "@/utils/config"; export default { name: 'CodeEditor', props: { value: { type: String, required: true }, readonly: { type: Boolean, default: false}, mode: {type: String, default: 'javascript'}, userWorker: {// Whether syntax checking is enabled, type: Boolean, default: true},}, mounted() {ace. Config. set('basePath', ACE_BASE_PATH)}, // }Copy the code

Added code hints to the CodeEditor:

addAutoCompletion(ace) { let acData = [ {meta: 'VForm API', caption: 'getWidgetRef', value: 'getWidgetRef()', score: }, {meta: 'VForm API', caption: 'getFormRef', value: 'getFormRef()', Score: 1}, //TODO:  let langTools = ace.require('ace/ext/language_tools') langTools.addCompleter({ getCompletions: function(editor, session, pos, prefix, callback) { if (prefix.length === 0) { return callback(null, []); }else { return callback(null, acData); }}})}Copy the code

The encapsulated CodeEditor looks like this (illustrated here with VForm) :

Now that we have the interaction events, we implement the component’s control API methods. This is a two-step process:

1. Obtain the component ref;

2. Call the methods in the methods property of the component;

First, add a refList Provider property to the form, inject inject into the component, and inject the component instance into the refList object when each component is created:

/ /... Inject: ['refList'], //... Created () {this.registertoreflist ()}, methods: { registerToRefList() { this.refList[this.field.options.name] = this }, //... Omitted here}Copy the code

Next, encapsulate a simple getWidgetRef method that gets the component instance by its name:

getWidgetRef(widgetName, showError) { let foundRef = this.refList[widgetName] if (! foundRef && !! showError) { this.$message.error('Ref not found') } return foundRef },Copy the code

Component methods in the Methods property can be called from the component instance, and component methods can be arbitrarily extended.

Implement a simple click event interaction:

/* The following code in the "Drink or beverage? Radio button onChange event to execute */ var DrinkWidget = this.getwidgetref ('alcoholChk') var drinkChkWidget = Enclosing getWidgetRef (' drinkChk) if (value = = = 1) {alcoholChkWidget. SetHidden (false) / / setHidden is custom API method, Control components to show or hide drinkChkWidget. SetHidden (true)} else {alcoholChkWidget. SetHidden. (true) drinkChkWidget setHidden (false)}Copy the code

The interaction effect is as follows:

It is important to note that the JS code in the interaction event is executed during the run of the form. The code is not Babel compiled. The process is to generate an anonymous Function with Function, pass in the specified parameters, and execute without using the inefficient and unsafe eval method.

If you want to debug interactive JS code, it is very simple. You just need to add a “debugger” to the code where the breakpoint is needed, and the code will be debugged (supported by all Chrome kernel browsers).

conclusion

To sum up, the general idea of implementing a programmable Vue dynamic form is as follows:

1. Design a form Schema specification and develop a drag-and-drop form designer based on the schema specification;

2. Expose component interaction events and implement component API methods as required;

3. Provide a javascript code editor with syntax highlighting and code hints;

4. The complex interactive logic of the form can be realized by calling the component API method in the interaction event;

5. Achieve your goal perfectly.

My new wheel VForm fully realizes the above functions, interested partners can experience to try.

>> Click here to experience it immediately <<

Detailed documentation: the VForm column

GitHub repository link: github.com/vdpadmin/VF…

Gitee Backup warehouse: gitee.com/vdpadmin/VF…