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

最近项目中遇到的需求是要操作大量的表单,之前的项目中有做过这方的研究,只不过是用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)
        }
    }
})

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容