本文旨在解決自定義千分位指令在el-input上初始化失效的問題
問題簡述
旨在解決社群問答 自定義指令修改底層 dom 元素,沒有效果
頁面首次載入後
要達成的效果
初始化時是沒有效果的,但是 input
丟失焦點時有效
解決思路
通常想用的是過濾器,但考慮到非普通值,是input節點值的處理,於是自然用起了自定義指令的鉤子函式bind
,通過它給節點繫結事件,處理格式化(XEUtils
是一個工具庫,章末給出連結),更改input.value
值
bind:只呼叫一次,指令第一次繫結到元素時呼叫,可進行初始化設定
Vue.directive('money', {
// 初始化設定
bind: function (el, binding, vnode, oldVnode) {
el.value = XEUtils.commafy(el.value, { digits: binding.arg })
el.onblur = () => {
el.value = XEUtils.commafy(el.value, { digits: binding.arg })
}
}
})
但馬上被打臉了,題主反饋報錯
報錯原因
- 上述程式碼假定繫結的
Dom
是渲染在原生input
標籤 el-input
是element-ui
自定元件,它的Dom
結構是下面這樣
<div class="el-input">
<input type="text" autocomplete="off" placeholder="請輸入內容" class="el-input__inner">
</div>
上述情況可在bind鉤子體內用 el.dataset.pardon = 'test'
證實。換而言之,v-money
在input
上可用,但在el-input
上無效,因其繫結的是div
而非 input
元素。
Dom佈局
<div id="app">
<h1>{{ money }}</h1>
<p>v-money:2 vue指令可見值:<input v-model=" money" v-money:2></p>
<p>thds(1)千分位過濾器: {{ money | thds(1) }}</p>
<el-input v-model="money" placeholder="請輸入內容" autosize v-money:2></el-input>
</div>
關鍵
渲染樹,子節點渲染完成回溯父樹,重複直到根,然後替換#app
節點內容bind
只在指令繫結節點時被呼叫一次,它不能保證子節點ok了。好在vue
提供了
inserted:被繫結元素插入父節點時呼叫
換而言之,若在渲染期,子節點編譯完成後插入父,然後父節點編譯,直到根。因此可以這樣幹,讓v-money
指令相容el-input
,而不僅僅是input
Vue.directive('money', {
inserted: function (el, binding) {
// 指令作用在 element-input 節點,對應原生 div.el-input (真實input節點的父節點)
if (el.tagName.toLocaleUpperCase() !== 'INPUT') {
el = el.getElementsByTagName('input')[0]
}
el.value = XEUtils.commafy(el.value, { digits: binding.arg })
el.onblur = () => {
el.value = XEUtils.commafy(el.value, { digits: binding.arg })
}
}
})
全域性過濾器
// 過濾器引數 通常與格式化,與值相關
Vue.filter('thds', function (value, digits) {
if (!value) return ''
return XEUtils.commafy(value, { digits })
})
資源
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<!-- 引入元件庫 -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xe-utils"></script>
原始碼
本作品採用《CC 協議》,轉載必須註明作者和本文連結