How to Implement Dynamic Form Addition, Deletion and Change Checking in vue2

Keywords: Javascript Vue Python JSON JQuery

Recent projects have encountered the need to manipulate a large number of forms. Previous projects have done this research, but only with jquery to operate.

Item A

Let's start with a brief description of the application scenarios in project A. There may be similar requirements for small partners. Project A is a project in the company's OA system, which is rendered by java's jsp. The requirement is to change: embedded in APP to display, front and back ends are separated, the content returned by back end can not be modified, but the back end colleagues have done the following interface processing, and returned a large amount of form data to the front end.

Each form has multiple fields representing its properties:

  • Editable or not

  • Form types (text, textarea, select, radio, checkbox, hidden, etc.)

  • Other forms associated with it

  • . . .
    Previous schemes used to judge form types and field attributes and call different UI components (such as time calendar selectors, etc.)

Item B

There are many fewer types of exhibition in the projects we encounter now. The first thing we think about is the same method, but this time we use the two-way binding of Vue.

Here's my experience in python back-end projects. If you're not interested, you can look directly at the final dynamic form section.

How to Introduce Vue in 1 Python Backend Project

Project B uses python's jinjia2 template, which is also {{}} to parse data. What happens in this case?

{% raw %}
<script type="text/x-template" id="dialog-wrap">
<div class="ms-dialog-wrap" v-show="visible">
  <div class="ms-dialog-inner">
    <div class="ms-dialog-title">{{title}}</div>
    <div class="ms-dialog-body">
      <div class="ms-dialog-content">
        <slot></slot>
      </div>
      <div class="ms-dialog-actions">
        <a class="ms-button" @click="cancelAction">cancel</a>
        <a class="ms-button ms-success" @click="confirmSuccess">Determine</a>
      </div>
    </div>
  </div>
  <div class="ms-overlayer" @click="cancelAction"></div>
</div>
</script>
{% endraw %}

The use of raw in jinjia2 prevents parsing internal code, so that we can introduce our vue template. Here is a dialog cartridge component I wrote.
2 Define Components
Here we take dialog pop-up window component as an example, and code it directly.

// dialog cartridge
Vue.component('ms-dialog', {
  name: 'ms-dialog',
  template: '#dialog-wrap',
  data: function () {
    return {
    }
  },
  props: {
    title: String,
    value: {
      type: Boolean,
      required: false
    }
  },
  computed: {
    visible: function () {
      return this.value
    }
  },
  watch: {
    visible: function (newVal) {
      if (newVal) {
        document.addEventListener('wheel', this.disabledScroll, false)
      } else {
        document.removeEventListener('wheel', this.disabledScroll, false)
      }
    }
  },
  methods: {
    confirmSuccess: function () {
      this.$emit('confirm-success')
    },
    cancelAction: function () {
      this.$emit('input', false)
    },
    disabledScroll: function (e) {
      e.preventDefault()
    }
  },
  beforeDestroy: function () {
    document.removeEventListener('scroll', this.disabledScroll, false)
  }
})

Dynamic Form Component

The general needs are:

  • A list can be added and deleted dynamically.

  • Each item in the list is a dynamic form with an uncertain number.

  • With submission function, submit or save the entire form

  • The saved form can be revised, added and deleted again after calling back through the interface and filling back the form.

1 How to generate dynamic forms
<template v-for="item in lists">
      <div class="list-item" v-if="list.type === 'input'">
        <label>User name</label>
        <input type="text" v-model="item.value" :value="list.defaultValue" class="form-control">
      </div>
      <div class="list-item" v-if="list.type === 'input'">
        <label>Password</label>
        <input type="text" v-model="item.value" :value="list.defaultValue" class="form-control">
      </div>
      <div class="list-item" v-if="list.type === 'textarea'">
        <label>Explain</label>
        <textarea rows="3" v-model="item.value" :value="list.defaultValue" class="form-control"></textarea>
      </div>
      <div class="list-item" v-if="list.type === 'select'">
        <label>Gender</label>
        <select v-model="list.value" :value="list.defaultValue">
            <option v-for="sub in list.source" :value="sub.value">{{sub.label}}</option>
        </select>
      </div>
</template>

The data format we discussed with the back end can be like this.

lists:  [{
  type: 'input',
  defaultValue: 'tom',
  value: 'tom'
}, {
  type: 'input',
  defaultValue: '123456',
  value: '123456'
}, {
  type: 'textarea',
  defaultValue: '123456',
  value: '123456'
}, {
  type: 'select',
  defaultValue: '0',
  value: '0',
  source: [{
    value: '1',
    label: 'male'
  }, {
    value: '1,
    label: 'female'
  }]
}]

Such a dynamic template is generated, and more types can be defined. This template data is usually cached. Because this data is also needed for the next addition operation.

Adding operations

The template above is just one of the dynamic lists.

<div v-for="book in books">
    <template v-for="item in book.lists">
      ......
    </template>
</div>
<div class="actions">
<button @click="add"></button>
</div>

The general method of add is:

methods: {
 add:  function () {
   this.books.push({
    lists:  [{
      type: 'input',
      defaultValue: 'tom',
      value: 'tom'
    }, {
      type: 'input',
      defaultValue: '123456',
      value: '123456'
    }, {
      type: 'textarea',
      defaultValue: '123456',
      value: '123456'
    }, {
      type: 'select',
      defaultValue: '0',
      value: '0',
      source: [{
        value: '1',
        label: 'male'
      }, {
        value: '1,
        label: 'female'
      }]
    }]
 })
 },

It's important to note that if the template's data is cached through the fields defined in the data attribute, you may encounter the value of the form after adding the operation, which will be linked with the value of one of the forms.
For specific reasons, guess is that the data here has become responsive, or that you can cache the template data by instantiating the value, and that may still be the case.
Specific code may be as follows:

var vm = new Vue({
    data: {
        books: [],
        cacheTemplate: null
    },
    methods: {
        getForms: function (argument) {
            this.$http.post(url, paras).then(res => {
                // This template data is cached here, and the data in cacheTemplate has become responsive
                this.cacheTemplate = res.body.data
                this.books.push(res.body.data) // Create the first dynamic form list

                // Or if you define it, there is no cacheTemplate value in the data. 
                // This definition is supposedly non-responsive, but it's not the case in practice, and it can affect other forms in the project as well.
                vm.cacheTemplate = res.body.data
                this.books.push(res.body.data) // Create the first dynamic form list
            }, res => {

            })
        },
        add: function () {
            // Here you will find that the value of your newly created form affects other forms
            // log out of this.cacheTemplate and you'll find that the values inside have changed
            this.books.push(this.cacheTemplate)
        }
    }
})

Here, the value of this.cacheTemplate changes, not clear, guessing the reason may become responsive, real-time monitoring and tracking in vue, Vue principle understanding of small partners can comment and tell me why.
Here's my solution: I don't care whether you are responsive or not, because it's the object, you can monitor the transformation, then I will change you into a string, okay?
Code directly:

var vm = new Vue({
    data: {
        books: [],
        cacheTemplate: null
    },
    methods: {
        getForms: function (argument) {
            this.$http.post(url, paras).then(res => {
                // This template data is also cached here, except that it becomes a string.
                this.cacheTemplate = JOSN.stringify(res.body)
                this.books.push(res.body) // Create the first dynamic form list
            }, res => {

            })
        },
        add: function () {
            // This is converted into a json object, and you find that the values in this.cacheTemplate are unchanged.
            var cacheTemplate = JSON.parse(this.cacheTemplate)
            this.books.push(cacheTemplate)
        }
    }
})

In this way, other form value transformation will not affect the data of my template, the problem has been solved.
If you think this article is good, you are welcome to comment. If you have any questions, let's talk and learn together.

Posted by PallaviDalvi on Tue, 25 Jun 2019 16:18:54 -0700