页面中有增加和编辑两个功能,由于弹窗样式都是一样的,于是将它拆分成一个子组件,父组件把状态传给子组件,子组件根据这个状态判断是做编辑操作还是新增操作.
编辑
添加
问题一:但是这样遇到了一个问题,在编辑时,只有第一次点编辑时,回显的数据才能正确显示。随后再点其他部门的编辑,数据显示不正确了,还是第一次点开的那个部门的数据.
原因:现在的代码中,获取部门详情这个动作在子组件的created中发出的,而created钩子只会执行一次:后续点击关闭弹层时,子组件被没有销毁,它只是隐藏了。
问题二:点击编辑之后再点击添加,添加表单中也会出现部门详情数据
原因是关闭弹窗时,子组件被没有销毁,它只是隐藏了,也没有清空里面表单的数据
我们先来看问题一数据不更新
问题一:回显数据不更新
解决方案一:在父组件中,给父组件中的el-dialog添加destroy-on-close属性
查阅了 Dialog 对话框 相关文档:
我们可以给它加上这个属性
<el-dialog
:title="isEdit?'编辑':'新增'"
:visible.sync="dialogVisible"
:close-on-click-modal="false"
:close-on-press-escape="false"
@close="resetForm"
>
<!-- element ui 中dialog的方法destroy-on-close,关闭时销毁 Dialog 中的元素 -->
<deptDialog
:id="fatherId"
:origin-list="originList"
:is-edit="isEdit"
destroy-on-close
@success="doSuccess"
@doClose="dialogVisible=false"
/>
</el-dialog>
解决方案二:直接在内容上加上属性 v-if="dialogVisible"
v-if="dialogVisible",关闭和打开弹层,销毁和创建组件
v-if会由false=>true,会触发beforeCreate,created,beforeMount,mounted 钩子。
由true=>false,会触发 beforeDestroy,destroyed 钩子。
所以我们只要给它加上v-if,取值为控制弹窗显示隐藏的值,我们打开弹窗时它就会重新重建,关闭弹窗的话就会销毁,
这样每次打开弹窗都会执行created钩子里的获取部门详情函数,问题就解决了
这样做虽然简单,但是它会有性能消耗,其中的ajax请求也会随着组件的创建和销毁重复执行
<el-dialog
:title="isEdit?'编辑':'新增'"
:visible.sync="dialogVisible"
:close-on-click-modal="false"
:close-on-press-escape="false"
@close="resetForm"
>
<!-- 解决数据不更新的问题:v-if="dialogVisible",关闭和打开弹层,销毁和创建组件 -->
<deptDialog
v-if="dialogVisible"
:id="fatherId"
:origin-list="originList"
:is-edit="isEdit"
@success="doSuccess"
@doClose="dialogVisible=false"
/>
</el-dialog>
解决方案三:在父组件中通过引用找到子组件
在父组件中,每次打开弹层时,找到子组件,要求它去发请求获取详情
添加ref引用:
<el-dialog
:title="isEdit?'编辑':'新增'"
:visible.sync="dialogVisible"
:close-on-click-modal="false"
:close-on-press-escape="false"
@close="resetForm"
>
<!-- 添加引用: ref="deptDialog" -->
<deptDialog
:id="fatherId"
ref="deptDialog"
:origin-list="originList"
:is-edit="isEdit"
@success="doSuccess"
@doClose="dialogVisible=false"
/>
</el-dialog>
在编辑时:找到子组件,要求它去发请求获取详情
// 编辑
doEdit(id) {
//isEdit设为true,表示当前状态为编辑
this.isEdit = true
this.fatherId = id
this.dialogVisible = true
// 解决数据不更新的问题: this.$refs.deptDialog.loadDepartDetail()
// 注意:由于DOM更新是异步的,此处要用$nextTick()
this.$nextTick(() => {
this.$refs.deptDialog.loadDepartDetail()
})
//或者放在宏任务里,如延时器中
// setTimeout(() => {
// this.$refs.deptDialog.loadDepartDetail()
// }, 0)
},
子组件:
created() {
//加载部门负责人列表数据
this.loadEmployeesSimples()
//如果为编辑状态,获取部门详情
if (this.isEdit) this.loadDepartDetail()
},
解决方案四:在子组件内监听id的变化(不推荐使用)
在子组件内部添加一个侦听器:监听当前id的变化
created() {
//加载部门负责人列表数据
this.loadEmployeesSimples()
//如果为编辑状态,获取部门详情
if (this.isEdit) this.loadDepartDetail()
},
watch: {
id: function(newVal, oldVal) {
//调用获取部门详情函数
this.loadDepartDetail()
}
},
//上面这种写法不会立即触发,还需在created()中调用一次,用下面这种方法就不需要在created()中调用了
//watch: {
// id: {
// handler: function(newVal, oldVal) {
// this.loadDepartDetail()
// },
// immediate: true
// }
// },
//immediate表示在watch中首次绑定的时候,是否执行handler,值为true则表示在watch中声明的时候,就立即 //执行handler方法,值为false,则和一般使用watch一样,在数据发生变化的时候才执行handler。所以当为 //true时 在created周期里就可以不用在写 已经在watch 中写过的方法了
但是这种方法在清空表单数据的时候会有一个bug,不推荐使用,后面会详细讲到.
解决方案五:给子组件添加key值
key就是给每一个vnode的唯一id,可以依靠key
更准确地拿到oldVnode中对应的节点。避免组件“原地复用”带来的副作用,加上key,可以让组件在数据变化时强制更新组件。
<el-dialog
:title="isEdit?'编辑':'新增'"
:visible.sync="dialogVisible"
:close-on-click-modal="false"
:close-on-press-escape="false"
@close="resetForm"
>
<!-- 给子组件添加key值 -->
<deptDialog
:id="fatherId"
:key="fatherId"
:origin-list="originList"
:is-edit="isEdit"
@success="doSuccess"
@doClose="dialogVisible=false"
/>
</el-dialog>
问题二:添加表单中也会出现部门详情数据
还是上面说到的问题,关闭弹层时,子组件被没有销毁,它只是隐藏了,所以我们需要手动清除表单中的数据.
(方案一添加destroy-on-close属性,方案二v-if="dialogVisible",方案五给子组件添加key值,不会出现这个问题)
分析:有如下三个操作都需要我们去重置表单
- 取消
- 确定
- 用户直接点击关闭
所以我们可以在Dialog的@close的回调中写一次代码就行了
父组件:
// 给父组件添加 @close="resetForm"
<el-dialog
:title="isEdit?'编辑':'新增'"
:visible.sync="dialogVisible"
:close-on-click-modal="false"
:close-on-press-escape="false"
@close="resetForm"
>
//给子组件添加 ref="deptDialog"
<deptDialog
v-if="dialogVisible"
ref="deptDialog"
:origin-list="originList"
:is-edit="isEdit"
@success="doSuccess"
@doClose="dialogVisible=false"
/>
</el-dialog>
// 重置子组件表单
resetForm() {
// 调用子组件中的重置表单方法
this.$refs.deptDialog.resetForm()
}
子组件:
// 在父组件中 dialog close时,来调用
resetForm() {
// resetFields是element-ui中的el-form组件提供一个api,它的作用是:
// 1. 重置表单数据
// 2. 清空校验结果(页面上红色的提示)
this.$refs.deptForm.resetFields()
}
清空表单数据的bug
问题重现
- 选中a部门,进行编辑,故意让编辑出错,在表单上出现错误
- 点击取消
- 再次对a部门进行编辑,发现数据没有显示出来
原因分析
上面的操作中,前后两次编辑的是同一个部门,所以子组件内对id的watch并没有执行,导致内容为空