最近的項目裏用上了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屬性就好了。