最近的项目里用上了vue和element-ui。vue这种轻量级渐进式框架的温馨自没必要说,但一直困扰着个人,是如何方便又优雅的弹出模态dialog...css
对于我这种在jquery出现以前就用document.getElementById敲代码的老顽固来讲,我始终不能彻底接受把dialog在编码期就写入模板的方式,下面是尤大在知乎某个相关问题的回答节选(全文请看https://www.zhihu.com/question/35820643):vue
为何必定要异步插入?
其实之前也有一些用户跟我纠结过这个问题,他们以为必定要在须要的时候建立这个组件才是符合他们思惟的作法。在我看来,这是没有理解『状态驱动的界面』的一种表现。
传统的命令式 (Imperative) 的思惟写出来的代码:jquery$('.open-modal').on('click', function () { var modal = new Modal() modal.$appendTo('body') modal.open() }) // 在 modal 内部还要处理关闭、销毁自身的逻辑状态驱动的思惟写出来的代码:element-ui
this.showModal = true // 关掉 this.showModal = false
不能否认,尤大所说的状态驱动确实是vue的精髓,可是在实际应用中,dialog每每须要直接在body下才能避免这样那样的问题,就好比本文要说的element-ui的el-dialog问题:若是你在一个el-dialog里,嵌套了另一个el-dialog,那么弹窗的遮罩层会相互影响,致使用户没法使用(新发布的element-ui 2.0已经解决了嵌套弹窗的问题,文档在这里http://element.eleme.io/#/zh-CN/component/dialog)。app
这就要求咱们把系统中全部可能出现的dialog,都预先放在vue的根组件中,但显然这是不合理的,根组件没法预知业务模块中将会出现的dialog。dialog应该和alert、messagebox、toast同样,提供方法级别的调用,但不知为什么element-ui为后者们提供了全局方法,但对dialog却没有。框架
本文的目的,就是为了分享一个为dialog提供全局方法的作法。这是我在csdn上看到的一篇文章,确实解决了个人问题,原文在这里:http://blog.csdn.net/zmy_coder/article/details/78042485dom
原理就是在方法被调用时,在body里create一个div,而且建立一个Vue实例,指定el属性为这个div。ecmascript
//dialog.js function makeDialog(option) { var dom = document.createElement('div'); document.getElementsByTagName('body')[0].appendChild(dom); let tpl = '\ <el-dialog \ :close-on-click-modal="false" \ :custom-class="customClass" \ :title="title" \ :visible.sync="show" \ :size="size" \ :before-close="handleClose" \ @close="close">\ <dialogContent @close="closeDialog" @confirm="confirmDialog" v-model="dialogData"></dialogContent>\ </el-dialog>'; var vue = new Vue({ el: dom, data: function () { return { title: option.title, size: option.size || 'small', show: true, dialogData: option.data, }; }, template: tpl, computed: { customClass(){ return `el-dialog--width-${option.size || 'auto'}`; } }, methods: { handleClose(done){ if (option.beforeClose) { option.beforeClose(done); } else { done(); } }, close() { if (option.close) { option.close(); } }, closeDialog(){ this.show = false }, confirmDialog(result){ this.show = false option.confirm && option.confirm(result) } }, components: { dialogContent: option.component, }, }); return vue; } export default { open(options){ return makeDialog(options) } }
在建立的这个Vue实例里,用到了el-dialog组件,而且具体的内容由外部调用者以component的形式传入,若是该component须要初始数据,须要为该component定义一个value属性,而且在调用open方法时,用options.data传入,而且能够设置在对话框beforeClose、close、confirm时的回调异步
用法示例:ui
对话框内容:
<!--SimpleDialogTest.vue--> <template> <div class="tutorial"> 请输入您的姓名 <input class="form-control" v-model="name"> <button type="button" class="btn btn-primary" @click="submit">肯定</button> <button type="button" class="btn btn-default" @click="cancel">取消</button> </div> </template> <style lang="scss" rel="stylesheet/scss" scoped> </style> <script type="text/ecmascript-6"> import dialog from '../../assets/js/dialog' export default{ props: { value: Object, }, data(){ return { name : this.value.name } }, methods: { submit(){ console.log('your name is ' + this.name) //do something if you like //... //关闭对话框 this.$emit('close'); //关闭对话框, 并回调调用者的option.confirm方法 // this.$emit('confirm', { // ... // }); }, cancel(){ this.$emit('close') } }, } </script>
调用方:
<!---调用方--> <template> <button @click="openDialog">弹出对话框</button> </template> <script type="text/ecmascript-6"> import dialog from '../assets/js/dialog' import SimpleDialogTest from 'SimpleDialogTest.vue' export default{ data(){ return{ } }, methods:{ openDialog(){ dialog.open({ title: '标题标题', size:'small', //可选项tiny/small/large/full, 对应el-dialog的size属性 component: SimpleDialogTest, data: { name: 'your name', }, // beforeClose: (done) => { // //点右上角关闭按钮后触发 // console.log('dialog is closing'); // done() // }, close: () => { //关闭后触发 console.log('dialog is closed') }, confirm: (result) => { //显式$emit('confirm')时触发 console.log('dialog is confirmed, and dialog result is ', result) } }) } } } </script>
关于option的size
el-dialog中size的四个选项tiny/small/large/full在实际应用中是不够的,有时候咱们但愿能为dialog能自适应内容组件的宽度,也就是说由内容组件来决定宽度,应该怎么作呢?
首先定义一个全局的css:
.el-dialog.el-dialog--width-auto{ width:auto !important; }
而后在调用dialog.open()的时候,不要指定size属性就好了。