Vue 管理表单编辑状态,样板代码



我的应用程序中有相当数量的表单,用户可以在其中选择编辑、还原或保存对对象的更改,这些更改最终会保存到后端。

与此非常相似:(在另一个问题中找到的代码(
https://jsfiddle.net/k5j6zj9t/22/

var app = new Vue({
el: '#app',
data: {
isEditing: false,
user: {
firstName: 'John',
lastName: 'Smith',
}
},
mounted() {
this.cachedUser = Object.assign({}, this.user);
},
methods: {
save() {
this.cachedUser = Object.assign({}, this.user);
this.isEditing = false;
},
cancel() {
this.user = Object.assign({}, this.cachedUser);
this.isEditing = false;
}
}
})

由于绑定v-model会立即更改基础对象,因此我必须首先创建对象的克隆。此外,我还需要保存一个数据成员,无论对象是否处于编辑状态。
将此代码乘以更多的表单和字段,我最终会得到太多的数据成员和大量的样板代码。

在像 django 这样的服务器框架中,模型在保存之前处于"临时状态",所以我可以这样编辑

user.first_name = 'aa' # temporary object in memory
user.save() # saved to the db

我的问题,vue 是否有模型组件/模式可以更好地处理此任务?
将保存模型状态的东西 - 即isEditing,自动克隆对象以进行表单编辑,还原更改等,
所以我不必为这么多对象编写这样的代码?

用途 作用域槽可能满足您的要求。

我的解决方案

  1. 使用一个插槽创建一个组件

  2. 然后这个插槽会将clonedValues绑定(如果closeMode为假,则clondedValues = values

    (
  3. 最后,在父组件中,使用作用域槽的属性生成模板,然后将其传递给槽。

像下面的演示:

Vue.component('child', {
template: `
<div>
<div>
<slot v-bind:values="clonedValues"></slot>
</div>
<p>
<button @click="saveAction(clonedValues)">Save</button>
<button @click="resetAction()">Reset</button>
</p>
</div>`,
props: {
'cloneMode': {
type: Boolean,
default: true
},
'values': {
type: Object,
default: () => { return new Object() }
}, 
'saveAction': {
type: Function,
default: function (newValues) {
this.$emit('save', newValues)
}
}, 
'resetAction': {
type: Function,
default: function () {
this.syncValues(this.values)
}
}
},
data() {
return {
clonedValues: {}
}
},
created: function () {
this.syncValues(this.values)
},
watch: {
values: {
handler: function (newVal) {
this.syncValues(newVal)
},
deep: true
},
cloneMode: function () {
this.syncValues(this.values)
}
},
methods: {
syncValues: function (newVal) {
this.clonedValues = this.cloneMode ? Object.assign({}, newVal) : newVal // if you'd like to support nested object, you have to deep clone
}
}
})
Vue.config.productionTip = false
app = new Vue({
el: "#app",
data: {
mode: true,
labels: ['id', 'name'],
childForm: {
'id': 1,
'name': 'test'
}
},
methods: {
saveForm: function (ev) {
Object.keys(this.childForm).forEach((item) => {
this.childForm[item] = ev[item]
})
// call backend to update the data
},
changeCurrentValue: function () {
this.childForm.id += '#'
this.childForm.name += '@'
}
}
})
<script src="https://unpkg.com/vue@2.5.16/dist/vue.js"></script>
<div id="app">
<p><button @click="mode=!mode">Mode: {{mode}}</button></p>
<p>Current: {{childForm}}  --<button @click="changeCurrentValue()">Change Current</button></p>
<child :values="childForm" @save="saveForm($event)" :clone-mode="mode">
<template slot-scope="slotProps">
<p>ID: <input v-model="slotProps.values['id']"/></p>
<p>Name: <input v-model="slotProps.values['name']"/></p>
</template>
</child>
</div>

请求的 OP 编辑:

  1. 将默认插槽更改为命名插槽=编辑,然后创建一个插槽=视图

  2. 添加了数据属性=编辑,如果为 true,则显示"编辑"插槽,如果为 false,则显示"查看"插槽。

  3. 在父组件中,为 slot=View设计模板。

像下面的演示:

Vue.component('child', {
template: `
<div>
<div v-show="editing">
<slot name="edit" v-bind:values="clonedValues"></slot>
<button @click="saveForm(clonedValues)">Save</button>
<button @click="resetAction()">Reset</button>
</div>
<div v-show="!editing">
<slot name="view"></slot>
<button @click="editing = true">Edit</button>
</div>
</div>`,
props: {
'values': {
type: Object,
default: () => { return new Object() }
}, 
'saveAction': {
type: Function,
default: function (newValues) {
this.$emit('save', newValues)
}
}, 
'resetAction': {
type: Function,
default: function () {
this.syncValues(this.values)
}
}
},
data() {
return {
editing: false,
clonedValues: {}
}
},
created: function () {
this.syncValues(this.values)
},
watch: {
editing: function (newVal) {
if(newVal) this.syncValues(this.values)
},
values: {
handler: function (newVal) {
if(this.editing) this.syncValues(newVal) //comment out this if don't want to sync latest props=values
},
deep:true
}
},
methods: {
syncValues: function (newVal) {
this.clonedValues = Object.assign({}, newVal) // if you'd like to support nested object, you have to deep clone
},
saveForm: function (values) {
this.saveAction(values)
this.editing = false
}
}
})
Vue.config.productionTip = false
app = new Vue({
el: "#app",
data: {
childForm: {
'id': 1,
'name': 'test'
}
},
methods: {
saveForm: function (ev) {
Object.keys(this.childForm).forEach((item) => {
this.childForm[item] = ev[item]
})
// call backend to update the data
},
changeCurrentValue: function () {
this.childForm.id += '#'
this.childForm.name += '@'
}
}
})
<script src="https://unpkg.com/vue@2.5.16/dist/vue.js"></script>
<div id="app">
<p>Current: {{childForm}}  --<button @click="changeCurrentValue()">Change Current</button></p>
<child :values="childForm" @save="saveForm($event)">
<template slot-scope="slotProps" slot="edit">
<h3>---Edit---</h3>
<p>ID: <input v-model="slotProps.values['id']"/></p>
<p>Name: <input v-model="slotProps.values['name']"/></p>
</template>
<template slot="view">
<h3>---View---</h3>
<p>ID: <span>{{childForm['id']}}</span></p>
<p>Name: <span>{{childForm['name']}}</span></p>
</template>
</child>
</div>

最新更新