DEV Community

MrChoke
MrChoke

Posted on • Originally published at Medium on

Vuetify and Cleave.js

หลังจากพยายามเขียนตัว Format input เองสำหรับ Vuejs แล้วพบว่าไม่ค่อยเวิร์คเท่าไหร่ เช่นตอนกรอกเครื่องหมายจุดแล้วตรวจสอบไม่ละเอียดลบได้บ้างไม่ได้บ้างเป็นต้น เลยหา lib คนอื่นมาใช้ดีกว่าเจอตัวนี้น่าสนใจดีอย่างแรกเลยก็ตอบโจทย์ที่กำลังหาพอดีเลยเอามาใช้แล้วพบว่ามีปัญหากับ vuetify เล็กน้อยเลยแงะจนมันใช้งานได้บันทึกไว้

DEMO

https://mrchoke.github.io/vuetify-cleave/

vuetify-cleave

Source

mrchoke/vuetify-cleave

Create VueJs & Veutify Project

vue create vuetify-cleave

cd vuetify-cleave

vue add vuetify
Enter fullscreen mode Exit fullscreen mode

Add Cleave.js

nosir/cleave.js

yarn add cleave.js
Enter fullscreen mode Exit fullscreen mode

Add Global Directive

main.js

import Cleave from 'cleave.js';

Vue.directive('cleave', {
    inserted: (el, binding) => {
        el.cleave = new Cleave(el, binding.value || {})
    },
    update: (el) => {
        const event = new Event('input', {bubbles: true});
        setTimeout(function () {
            el.value = el.cleave.properties.result
            el.dispatchEvent(event)
        }, 100);
    }
})
Enter fullscreen mode Exit fullscreen mode

link: https://github.com/nosir/cleave.js/blob/master/doc/vue.md

ตอนนี้ Vue จะเห็น directive cleave แล้วลองสร้าง text field ดูครับ

<v-text-field v-model="comma" label="Number with Comma" **v-cleave="{ numeral: true, numeralThousandsGroupStyle: 'thousand' }"** > </v-text-field>
Enter fullscreen mode Exit fullscreen mode

vuetify text field error

ถ้าลอง input จะมี error ขึ้นมา แต่ถ้าใช้ input ปกติของ HTML จะไม่มีปัญหา

<input type="text" v-model="comma2" **v-cleave="{ numeral: true, numeralThousandsGroupStyle: 'thousand' }"** />
Enter fullscreen mode Exit fullscreen mode

input ของ Vuetify vs Native HTML

ลองค้นหาข้อมูลพบว่า input ของ vuetify นั้นเป็นแบบ component สิ่งที่เห็นนั้นประกอบไปด้วย element ต่างๆ มากมายมันไม่ใช่ input ที่แท้จริงผมเลยไปแกะ directive ที่เค้าทำมาแล้วใช้ได้กับ Vuetify ซึ่งเค้าจำหา element ที่แท้จริงส่งให้นั่นเองเอาแบบง่ายๆ แก้ที่ main.js โดยสร้าง function เพิ่มมาแล้วเรียกใช้

**function getInput(el) {  
  if (el.tagName.toLocaleUpperCase() !== 'INPUT') {  
    const els = el.getElementsByTagName('input')  
    if (els.length !== 1) {  
      throw new Error(`v-cleave requires 1 input, found ${els.length}`)  
    } else {  
      el = els[0]  
    }  
  }  
  return el  
}**  

Vue.directive('cleave', {
  inserted: (el, binding) => {
**el = getInput(el)**
    el.cleave = new Cleave(el, binding.value || {})
  },
  update: el => {
**el = getInput(el)**
    const event = new Event('input', { bubbles: true })
    setTimeout(function() {
      el.value = el.cleave.properties.result
      el.dispatchEvent(event)
    }, 100)
  }
})
Enter fullscreen mode Exit fullscreen mode

ทำงานได้เหมือนกันละ

TypeScript

สำหรับ TypeScript จะมีปัญหาเรื่อง properties ที่ Cleave.js แปะเข้าไปใน HTMLElement ทำให้เกิดการเตือนหรืออาจจะใช้งานไม่ได้

ขั้นแรกเพิ่ม @type/cleave.js ก่อน

yarn add -D @types/cleave.js
Enter fullscreen mode Exit fullscreen mode

หลังจากนั้นให้สร้าง interface โดย extents HTMLElement เช่น

import Cleave from 'cleave.js'
import { CleaveOptions } from 'cleave.js/options'

class Cl extends Cleave {
  properties?: Record<string, string>
  constructor(selector: string | HTMLElement, options: CleaveOptions) {

  super(selector, options)

 }
}

export interface HTMLElementA extends HTMLElement {
  cleave?: Cl
  value?: string
}
Enter fullscreen mode Exit fullscreen mode

แล้วแก้ในส่วนของการประกาศ directive ใน main.ts

function getInput(el: **HTMLElementA** ) {
    if (el.tagName.toLocaleUpperCase() !== 'INPUT') {
      const els = el.getElementsByTagName('input')
      if (els.length !== 1) {
        throw new Error(`v-cleave requires 1 input, found ${els.length}`)
      } else {
        el = els[0]
      }
    }
    return el
  }

Vue.directive('cleave', {
  inserted: (el: **HTMLElementA** , binding) => {
    el = getInput(el)

el.cleave = new Cleave(el, binding.value || {})
  },
  update: (el: **HTMLElementA** ) => {
    el = getInput(el)
    const event = new Event('input', { bubbles: true })
    setTimeout(function() {
      el.value = **el.cleave?.properties?.result**
      el.dispatchEvent(event)
    }, 100)
  }
})
Enter fullscreen mode Exit fullscreen mode

ก็จะประมาณนี้ครับ

ตัวอย่าง

Top comments (0)