I apologize in advance, but it’s been a month since I wrote my last article. I have had too many things to do in the past month, and I am not in good physical condition. Every weekend, I always think that I should write an article, but then I think that no one will read it, so why do I have to be so tired? Until a few days ago, when I saw some friends leave messages urging me to do more, I realized that since I was writing a series of articles, there is no reason to give up halfway, only to finish it with energy. In fact, I just write technical articles of the new, come up to write a series of articles, is really not a good choice; Fortunately, as this series comes to an end, the next few articles are examples of real world scenarios that can serve as a reference for vUE forms practices.

Code address: gitee.com/wyh-19/supe… This article is based on the following code: Essay-9

Series of articles:

  • Vue + Element Large Forms solution (1)– Overview
  • Vue + Element Large Forms solution (2)– Form splitting
  • Vue + Element Large Forms Solution (3)– Anchor Components (part 1)
  • Vue + Element Large Form Solution (4)– Anchor Component (2)
  • Vue + Element Large Forms solution (5)– Validation identifier
  • Vue + Element Large Forms solution (6)– Automatic identification
  • Vue + Element Large Forms solution (7)– Form form
  • Vue + Element Large Form Solution (8)– Data comparison (1)
  • Vue + Element Large Form Solution (9)– Data comparison (2)
  • Vue + Element Large Forms solution (10)– Form communication and dynamic forms

preface

The last one realized the basic data comparison, but the scene is relatively simple, comparison is the data of the text class control. This paper will supplement the data comparison of some complex controls, such as SELECT, radio, checkbox, etc., all of which have a common feature, that is, value is not suitable for direct display, and can only be compared after it is converted into corresponding text.

The preparatory work

Go to the form1.vue file and add some common complex controls as follows:

<el-form-item label="Degree" class="field-wrapper">
  <el-select v-model="formData.education" v-compare:education="oldFormData">
    <el-option v-for="item in educationList" :key="item.value" :value="item.value"
               :label="item.label"></el-option>
  </el-select>
</el-form-item>
<el-form-item label="Gender" class="field-wrapper">
  <el-radio-group v-model="formData.gender" v-compare:gender="oldFormData">
    <el-radio :label="1">female</el-radio>
    <el-radio :label="2">male</el-radio>
  </el-radio-group>
</el-form-item>
<el-form-item label="Hobby" class="field-wrapper">
  <el-checkbox-group v-model="formData.hobby" v-compare:hobby="oldFormData">
    <el-checkbox v-for="item in hobbyList" :key="item.value" :label="item.value">
      {{ item.label }}
    </el-checkbox>
  </el-checkbox-group>
</el-form-item>
Copy the code

Add the required responsive data to the corresponding data:

educationList: [
    {
      label: 'Graduate student'.value: 1
    },
    {
      label: 'bachelor'.value: 2
    },
    {
      label: 'college'.value: 3}].hobbyList: [{label: 'read'.value: 1
    },
    {
      label: 'Play games'.value: 2
    },
    {
      label: 'movement'.value: 3}].Copy the code

Find the demo.js file and add the test data to return as follows:

export function ajaxGetData() {... Omit the resolve ({name: 'wyh'.age: 30.education: 1.gender: 1.hobby: [1.3].company: 'aaa'
    }
  )
...省略
}

export function ajaxGetOldData() {... Omit the resolve ({name: 'wyh19'.age: 30.education: 2.gender: 2.hobby: [2.3].company: 'bbb'
    }
  )
...省略
}
Copy the code

In the form assembly file index.vue, find the resolveDataToMap method and add field handling:

resolveDataToMap(data) {
  const form1 = {
    name: data.name,
    age: data.age,
    education: data.education,
    gender: data.gender,
    hobby: data.hobby } ... Omit}Copy the code

After the preparation, enter the comparison page and the effect is as follows:

Obviously, these value-type data comparison results are not satisfactory, which is exactly the problem to be solved in this paper. Let’s start formally.

Implementation approach

By comparing the old value with the new value, we can distinguish whether the data has changed or not. However, the text content cannot be directly displayed like the field of the text class, and the text needs to be derived according to the value. Therefore, the function of text parsing needs to be added in the instruction. V -compare: field name =”oldFormData” is not enough information, we need to expand to add additional information, such as v-compare: field name. Comparison method =”{oldFormData, other information}”. As it involves the internal instructions, some fields need to be fixed into norms. For example, in the current example, select and radio need to map value to label for display, and map is used for comparison, and map is also used as the field name for other information fields. Map =”{oldFormData,map:{… }} “.

The specific implementation

The realization of the radio

Radio is relatively the simplest, so start from here to realize the above ideas. To change the gender of this field the comparison instruction uses the code:

<el-radio-group v-model="formData.gender"
                v-compare:gender.map={oldFormData,map:{1:' female ',2:' male '}}">
    <el-radio :label="1">female</el-radio>
    <el-radio :label="2">male</el-radio>
</el-radio-group>
Copy the code

In this case, enter the v-compare.js file and add the map type processing. Since the use form of the instruction is different from the previous one, and I do not want to change the original writing method, I need to isolate the instruction inside and keep the original logic unchanged. Remove the modifiers from the binding parameter and determine which logic to use based on their content as follows:

componentUpdated(el, binding, vnode) {
    const { value, oldValue, arg, modifiers } = binding
    if (modifiers.map) {
      // Map type logic is implemented here

    } else {
      // === the original text type alignment logic remains unchanged ====
      // oldFormData is compared only when there is data from no data
      // Avoid too many invalid alignments
      if(! oldValue && value) {// The comparison function is available when entering this if judgment
        // The latest data, that is, the current binding value in the V-model
        const lastModel = vnode.data.model.value
        // oldFormData[arg]
        const beforeModel = value[arg]
        // If the two data are different, it is not used here! = =
        if(lastModel ! == beforeModel) {// Label it
          markDiffrent(el, beforeModel)
        }
      }
    }
}
Copy the code

Write a similar comparison logic code in the map’s judgment body as follows:

if (modifiers.map) {
    // Map type logic is implemented here
    // Get oldFormData before and after the directive update
    const oldV = oldValue.oldFormData
    const v = value.oldFormData
    // Get the map information
    const map = value.map
    // Compare oldFormData twice to avoid unnecessary invalid comparisons
    if(! oldV && v) {const lastModel = vnode.data.model.value
    const beforeModel = v[arg]
        if(lastModel ! == beforeModel) {// Map directly from the map to the corresponding text
          markDiffrent(el, map[beforeModel])
        }
    }
}
Copy the code

At this point, we can see that the radio has realized the comparison effect, as shown below (the style problem is not discussed here, you can adjust it according to your needs) :

The realization of the select

In this example, radio and SELECT are essentially the same, the only difference being that the radio options are enumerated and the SELECT options are iterated, so the main work here is to get the map information. In the super-form-mixin.js file, write a public method that handles the conversion as follows:

/ * * * convert the select options for comparison instruction need to map type *, for example: [{value: 1, the label: 'a'}] = = = > {1} : a * /
composeOptions(options = [], value = 'value', label = 'label') {
  const map = {}
  options.forEach(item= > {
    map[item[value]] = item[label]
  })
  return map
},
Copy the code

At this time to modify the select compare instructions for v – compare: education. The map = “{oldFormData, map: composeOptions (educationList)}” when comparing the results as follows:

The realization of the checkbox

Checkbox = arrayMap = arrayMap = arrayMap = arrayMap = arrayMap = arrayMap = arrayMap Checkbox comparison instruction using code v – compare: hobby. ArrayMap = “{oldFormData, map: composeOptions (hobbyList)} in modifiers. The map following add new branches, the code is as follows:

else if (modifiers.arrayMap) {
  // arrayMap type logic is implemented here
  // Get oldFormData before and after the directive update
  const oldV = oldValue.oldFormData
  const v = value.oldFormData
  // Get the map information
  const map = value.map
  // Compare oldFormData twice to avoid unnecessary invalid comparisons
  if(! oldV && v) {const lastModel = vnode.data.model.value
    const beforeModel = v[arg]
    if(! compareEasyArray(lastModel, beforeModel)) {// Map directly from the map to the corresponding text
      markDiffrent(el, getArrayMapResult(beforeModel, map))
    }
  }
} 
Copy the code

I did not optimize the code for the convenience of writing here. You can use switch branch to expand and optimize the duplicate code according to your own habits. The difference with the previous comparison is that:

  1. It’s not easy to uselastModel ! == beforeModelTo see if two values are different, we usecompareEasyArrayMethod, the requirement here is to determine whether the array is different not in order, but whether there are different items.
  2. Not easymap[beforeModel]Get the text, but use itgetArrayMapResultmethods

The following implements both methods:

function compareEasyArray(arr1, arr2) {
  // Each item in the array is of a simple type and does not compare in order
  const arr1ToString = arr1.sort().join(', ')
  const arr2ToString = arr2.sort().join(', ')
  return arr1ToString === arr2ToString
}
function getArrayMapResult(arr, map) {
  const result = arr.map(item= > map[item])
  return result.join(', ')}Copy the code

The effect is as follows:

At this point we have basically achieved our goal, if you want to do more perfect, just need to use the current thinking and their own fantastic ideas.

Expand supplementary

In practice, there are far more than these comparison types, and the following code is not demonstrated in the demo, but is simply a note for quick lookup if needed later.

  1. Value field, but the back end directly returns the corresponding label, or the front end finds laebL and does not want to map inside the instruction, then the label comparison method can be addedV - compare: the field name. The label = "{oldFormData, label: xxLabel}"

Corresponding instruction parsing method:

if (modifiers.label) {
  const oldV = oldValue.oldFormData
  const v = value.oldFormData
  const label = value.label
  if(! oldV && v) {const lastModel = vnode.data.model.value
    const beforeModel = v[arg]
    if(lastModel ! == beforeModel) { markDiffrent(el, label) } } }Copy the code
  1. Select-tree control data, need to recursively search the tree node
<v-tree-select v-model="formData.respUnitId"
               v-compare:respUnitId.tree="{oldFormData,tree:{options:$store.state.base.unitUserTreeData,id:'id',label:'name',children:'childList'}}"
               :options="$store.state.base.unitUserTreeData"
               no-results-text="There is no such department."
               :normalizer="(node)=>({id:node.id,label:node.name,children:node.childList})" />
Copy the code

Corresponding instruction parsing code:

if (modifiers.tree) {
  const oldV = oldValue.oldFormData
  const v = value.oldFormData
  const tree = value.tree
  if(! oldV && v) {const lastModel = vnode.data.model.value
    const beforeModel = v[arg]
    if(lastModel ! == beforeModel) {const { options, id, label, children } = tree
      const beforeLabel = getLabelFromTree(beforeModel, options, id, label, children)
      markDiffrent(el, beforeLabel)
    }
  }
}
Copy the code

Implement getLabelFromTree recursively:

function getLabelFromTree(value, tree, idKey, labelKey, childrenKey) {
  let result = ' '
  if(! value || ! tree || ! tree.length) {return result
  }
  for (let i = 0; i < tree.length; i++) {
    if (tree[i][idKey] === value) {
      result = tree[i][labelKey]
    } else {
      result = getLabelFromTree(value, tree[i][childrenKey], idKey, labelKey, childrenKey)
    }
    if (result) {
      break}}return result
}
Copy the code

Through the above example, I want to illustrate that any complex value control can be converted into a comparison result that can be displayed. If it is really difficult to solve the situation, you can actively obtain the label in the outer layer and pass the label into the form of display.

At this point, the field alignment function of a normal form has been fully implemented. I’ll show you how some complex types of forms can be implemented and how they can communicate interactively across forms. Thank you for reading, and your comments are welcome!