前端经常做的就是新增和修改表单提交,受组件化的影响,一般我们都会将表单进行复用,新增和修改共用一个drawer或者dialog。
接下来谈谈几种共用的方式
第一种方式: 组件的open由组件内部控制
子组件:
js
// template
<el-drawer
:title="title"
:visible.sync="drawer"
:before-close="close"
custom-class="add-drawer">
<el-form> 内容</el-form>
</el-drawer>
// data
drawer: false,
// methods
open() {
this.drawer = !this.drawer
},
父组件:
父组件要做的事情:调用子组件的open、传递title的值、传递row的值
js
// template
<add-drawer
ref="addDrawer"
:title="title"
:row="row">
</add-drawer>
// methods
addDrawer() {
this.row = {}
this.title = '新增'
this.$refs.addDrawer.open()
},
editDrawer(row) {
this.row = row
this.title = '修改'
this.$refs.addDrawer.open()
},
此时如果在子组件直接处理row,像下面的代码是会出现:点击新增,drawer打开,el-form空白(合理);点击修改,drawer打开,el-form空白(不合理,本质应该有值)。
js
open() {
this.drawer = !this.drawer
if (JSON.stringify(this.row) !== '{}') {
// 修改
this.form = JSON.parse(JSON.stringify(this.row))
} else {
// 新增
this.$refs.form && this.$refs.form.resetFields()
}
},
为什么会出现这个原因,是因为vue是异步更新的
txt
父组件调用addDrawer() -> 调用子组件open方法并修改row -> 父组件重新渲染 -> 子组件重新获得props
在第二步,子组件拿不到新的row,所以提供了nextTick的解决方案:
js
open() {
this.drawer = !this.drawer
this.$nextTick(() => {
if (JSON.stringify(this.row) !== '{}') {
// 修改
this.form = JSON.parse(JSON.stringify(this.row))
} else { // 新增的时候 row是{}
// 新增
this.$refs.form && this.$refs.form.resetFields()
}
})
},
nextTick是更新后再调用,那就可以在open中拿到最新的值
还有一种解决方案就是借助watch,在watch中进行逻辑处理
js
watch: {
row: {
immediate: true,
deep: true,
handler() {
if (JSON.stringify(this.row) !== '{}') {
// 修改
this.form = JSON.parse(JSON.stringify(this.row))
} else {
// 新增
this.$refs.form && this.$refs.form.resetFields()
}
}
}
},
// methods
open() {
this.drawer = !this.drawer
}
第二种方式:不使用props传递,直接上参数
子组件:
js
// template
<el-drawer
:title="title"
:visible.sync="drawer"
custom-class="add-drawer">
<el-form> 内容</el-form>
</el-drawer>
// data
drawer: false,
// methods
open() {
this.drawer = !this.drawer
},
父组件:
js
addDrawer() {
this.title = '新增'
this.$refs.addDrawer.open()
},
editDrawer(row) {
this.title = '修改'
this.$refs.addDrawer.open(row)
},
这样子组件处理数据,是必然可以拿到row的,因为row是通过参数传递的,不是通过props
js
// 子组件的open
open(row) {
this.drawer = !this.drawer
if (row) {
this.form = JSON.parse(JSON.stringify(row))
} else { // 新增的时候 row是undefined
this.$refs.form && this.$refs.form.resetFields()
}
},
第三种方式:组件的open由父组件控制
子组件:
js
//template
<el-dialog
:title="title"
:visible.sync="dialogVisble"
width="500px"
@close="close"
@open="open">
<el-form> 内容</el-form>
</el-dialog>
// script
props: {
title: {
type: String
},
visible: {
type: Boolean
},
form: {
type: Object
}
}
父组件:
js
// template
<add-dialog
ref="addDialog"
:title="title"
:visible="dialog"
:form="row"
@closed="close">
</add-dialog>
// methods
addDialog() {
this.title = '新增'
this.row = {}
this.dialog = true
},
editDialog(row) {
this.title = '修改'
this.row = row
this.dialog = true
},
子组件拿到父组件的值,需要做的操作
js
watch: {
visible(v) {
this.dialogVisble = v
},
form(v) {
this.formData = JSON.parse(JSON.stringify(v))
}
},
子组件还处理了open和close回调方法,主要是为了解决form检验和关闭时报错子组件操作父组件数据的问题
js
// 如果没有open 那么出现一个问题,就是点击修改,一切正常,然后关闭。再点击新增,此时新增的校验会全部展示
open() {
this.$refs.form && this.$refs.form.resetFields()
},
// 如果没有close 那么会出现一个问题,就是点击dialog自带的x,会报错子组件修改父组件的错误,因为子组件的visible是父组件传值的,所以提交emit父组件去关闭
close() {
this.$emit('closed')
},