自定義指令在 el-input 節點上無效解決方案

pardon110發表於2020-07-09

本文旨在解決自定義千分位指令在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 })
            }
        }
    })

但馬上被打臉了,題主反饋報錯

報錯原因

  1. 上述程式碼假定繫結的 Dom 是渲染在原生 input 標籤
  2. el-inputelement-ui自定元件,它的Dom結構是下面這樣
<div class="el-input">
    <input type="text" autocomplete="off" placeholder="請輸入內容" class="el-input__inner">
</div>

上述情況可在bind鉤子體內用 el.dataset.pardon = 'test'證實。換而言之,v-moneyinput上可用,但在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>

原始碼

el-input 自定義千分位指令 v-money

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章