美文网首页
vue2 中如何实现动态表单增删改查

vue2 中如何实现动态表单增删改查

作者: mujiaxiansheng | 来源:发表于2017-06-01 19:30 被阅读3892次

    最近项目中遇到的需求是要操作大量的表单,之前的项目中有做过这方的研究,只不过是用jquery来操作。

    项目A

    先简单说说以前项目A中的应用场景,可能有小伙伴儿也遇到相同的需求。A项目是公司的OA系统中有的项目,是用java的jsp渲染的页面,需求是要改成:嵌入APP中显示,前后端分离, 后端返回的内容,还不能修改, 只是后端同事做了下接口处理,返回给前端的是一大堆的表单数据。

    每个表单都有多个字段表示它的属性:

    • 是否可编辑
    • 表单类型 (text, textarea, select, radio, checkbox, hidden等 )
    • 与之联动的其他表单
    • 。。。
      之前的方案就是各个表单类型和字段属性进行判断,调用不同的UI组件(如时间日历选择器等)

    项目B

    现在遇到的项目,展示类型少很多,第一个想到的就是同样的方法,不过这次使用的是Vue的双向绑定。

    以下是我在python后端项目中的经验,如果没有兴趣可以直接看最后的动态表单部分

    1 python 后端项目中如何引入Vue

    项目B用的是python的jinjia2的模板, 同样都是 {{}} 去解析数据,这种情况下怎么办呢?

    {% 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">取消</a>
            <a class="ms-button ms-success" @click="confirmSuccess">确定</a>
          </div>
        </div>
      </div>
      <div class="ms-overlayer" @click="cancelAction"></div>
    </div>
    </script>
    {% endraw %}
    

    jinjia2中使用 raw 可以阻止解析内部的代码,这样就可以引入我们的vue模板了,这里是我写的一个dialog弹框的组件
    2 定义组件
    这里以dialog弹窗组件为例子,直接上代码

    // dialog弹框
    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)
      }
    })
    

    动态表单组件

    一般的需求是:

    • 一个列表,可以实现列表的动态添加,删除。
    • 列表中的每一项是动态的表单,表单个数不确定,
    • 有提交功能,提交或者可以保存整个表单
    • 保存的表单,通过接口调回后,回填表单,还可以再次修改、增加、删除等
    1 如何生成动态表单
    <template v-for="item in lists">
          <div class="list-item" v-if="list.type === 'input'">
            <label>用户名</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>密码</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>说明</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>性别</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>
    

    我们的与后端商量好的数据格式可以是这样的;

    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: '男'
      }, {
        value: '1,
        label: '女'
      }]
    }]
    

    这样一个动态模板就生成了,其他更多类型都可以定义。这份模板数据,一般是需要缓存的。因为接下来的 添加操作也需要这份数据。

    添加操作

    上面的template只是其中一个动态列表。

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

    add的方法一般是:

    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: '男'
          }, {
            value: '1,
            label: '女'
          }]
        }]
     })
     },
    

    这里需要注意的是,如果这份模板的数据,你是通过在data属性中定义的字段去缓存的,那有可能遇到的是你通过添加操作之后的表单的值会,会随着其中的某个表单的值一起联动。
    具体原因,猜测是这里的数据已经是变成响应式的了, 又或者你 通过实例化后的值去缓存这份模板数据,可能结果还是这样。
    具体代码可能是这样的:

    var vm = new Vue({
        data: {
            books: [],
            cacheTemplate: null
        },
        methods: {
            getForms: function (argument) {
                this.$http.post(url, paras).then(res => {
                    // 此处缓存了这份模板数据,cacheTemplate中的数据已经变成响应式的了, 此处cacheTemplate是引用类型
                    this.cacheTemplate = res.body.data
                    this.books.push(res.body.data) // 创建第一动态表单列表
                }, res => {
    
                })
            },
            add: function () {
                // 因为是引用类型,所以cacheTemplate属性值的变化必然影响其他和它相同属性的值
                         this.books.push(this.cacheTemplate)
            }
        }
    })
    

    此处的解决方法: 是要换成深度复制
    直接上代码:

    var vm = new Vue({
        data: {
            books: [],
            cacheTemplate: null
        },
        methods: {
            getForms: function (argument) {
                this.$http.post(url, paras).then(res => {
    
                    // 此处同样缓存了这份模板数据,不同的是把它变成了字符串, 深度复制
                    this.cacheTemplate = JOSN.stringify(res.body)
                    this.books.push(res.body) // 创建第一动态表单列表
                }, res => {
    
                })
            },
            add: function () {
                // 此处转化成json对象
                var cacheTemplate = JSON.parse(this.cacheTemplate)
                this.books.push(cacheTemplate)
            }
        }
    })
    

    如果觉得本文不错的话,欢迎点赞。如有问题, 大家一起交流和学习

    相关文章

      网友评论

          本文标题:vue2 中如何实现动态表单增删改查

          本文链接:https://www.haomeiwen.com/subject/fhiefxtx.html