写一个几个文字加个el-select框的玩意,有必要用el-dialog这么麻烦的东西么。写个el-dialog还要加一堆乱七八糟的属性如:width、top、append-to-body、close-on-click-modal,还有el-dialog默认是不带footer按钮的,还需要在footer里加上el-button确定才行。另外内部的样式还要重新写一遍,el-messagebox里面的样式不香么。这很明显不是我想要的开发模式。
毕竟比尔盖茨说过:能用service
做的 dialog,就尽量不要用template
做的 dialog
言归正传。于是,我开始了用el-messagebox代替el-dialog的开发征程。看了一下element官方文档,对话框内容message可以用$createElement去实现
vue2的写法
那么我就按照官网的写法来做:
<template>
<el-button @click="open">打开弹窗</el-button>
</template>
<script>
export default {
methods: {
async open() {
let sex = '未知'
const h = this.$createElement
await this.$msgbox({
title: '提示',
message: h('p', [
'选择性别',
h('el-select', {
props: {
value: sex
},
on: {
change: val => (sex = val)
},
style: {
marginLeft: '1em'
}
}, ['男', '女', '未知'].map(sex => {
return h('el-option', {
key: sex,
props: {
label: sex,
value: sex
}
})
}))
])
})
alert(sex)
}
}
}
</script>
效果图:
可是问题出现了,改变不了select的值???
百思不得其姐,那只好去看看element源码呗,发现原来是将message赋值给$slots.default
了
那么我立刻就想到message是vnode
的话,何不去用VueComponent
去试试呢?那么就可以改造成:
<template>
<el-button @click="open">打开弹窗</el-button>
</template>
<script>
import Vue from 'vue'
const MessageboxSelect = Vue.component('messagebox-select', {
data() {
return {
value: '未知'
}
},
render() {
const h = this.$createElement
return h('p', [
'选择性别',
h('el-select', {
props: {
value: this.value
},
on: {
change: val => (this.value = val)
},
style: {
marginLeft: '1em'
}
}, ['男', '女', '未知'].map(sex => {
return h('el-option', {
key: sex,
props: {
label: sex,
value: sex
}
})
}))
])
}
})
export default {
methods: {
async open() {
const h = this.$createElement
await this.$msgbox({
title: '提示',
message: h(MessageboxSelect)
})
}
}
}
</script>
但是这样会引发一个问题,弹窗消失后再打开会出现之前保留的状态,而不是重新生成初始状态
那么就好办了,只要把Vue.component()
这一步放到open
里面就可以了
<template>
<el-button @click="open">打开弹窗</el-button>
</template>
<script>
import Vue from 'vue'
export default {
methods: {
async open() {
let sex = '未知'
const h = this.$createElement
await this.$msgbox({
title: '提示',
message: h(Vue.component('messagebox-select', {
data() {
return {
value: sex
}
},
render() {
const h = this.$createElement
return h('p', [
'选择性别',
h('el-select', {
props: {
value: this.value
},
on: {
change: val => (sex = this.value = val)
},
style: {
marginLeft: '1em'
}
}, ['男', '女', '未知'].map(sex => {
return h('el-option', {
key: sex,
props: {
label: sex,
value: sex
}
})
}))
])
}
}))
})
alert(sex)
}
}
}
</script>
搞掂!!
附上vue3的写法
createVNode的形式(vue2叫createElement,3改成叫createVNode)
<template>
<el-button @click="open">打开弹窗</el-button>
</template>
<script>
import {ref,createVNode} from 'vue'
import {ElMessageBox, ElSelect, ElOption} from 'element-plus'
export default {
setup() {
async function open() {
let sex = '未知'
const h = createVNode
const elSelect = h(ElSelect,
{
modelValue: sex,
'onUpdate:modelValue': val => (sex = elSelect.component.props.modelValue = val),
style: {
marginLeft: '1em'
}
},
{
default: () => ['男', '女', '未知'].map(sex => {
return h(ElOption, {
key: sex,
label: sex,
value: sex
})
})
})
await ElMessageBox({
title: '提示',
message: h(createVNode('p', {}, [
'选择性别',
elSelect
]))
})
alert(sex)
}
return {open}
}
}
</script>
最关键的地方是:'onUpdate:modelValue': val => (sex = elSelect.component.props.modelValue = val)
,否则el-select
的v-model
效果就不生效。我找了所有的文档和stackoverflow、github tissue这些都没找到解决办法,最终自己去vnode
的属性里面找到了嘿嘿(花了我好多时间)。
jsx的写法
<template>
<el-button @click="open">打开弹窗</el-button>
</template>
<script>
import {ref,createVNode} from 'vue'
import {ElMessageBox, ElSelect, ElOption} from 'element-plus'
export default {
setup(props, ctx) {
async function open() {
let sex = '未知'
const h = createVNode
const elSelect = h(<ElSelect
modelValue={sex}
onUpdate:modelValue={val => (sex = elSelect.component.props.modelValue = val)}
style={{marginLeft: '1em'}}
>
{
['男', '女', '未知'].map(sex => {
return h(<ElOption key={sex} label={sex} value={sex}></ElOption>)
})
}
</ElSelect>)
await ElMessageBox({
title: '提示',
message: h(<p>
选择性别{elSelect}
</p>)
})
alert(sex)
}
return {open}
}
}
</script>