Vuetify and Cleave.js
MrChoke
Posted on August 30, 2020
หลังจากพยายามเขียนตัว Format input เองสำหรับ Vuejs แล้วพบว่าไม่ค่อยเวิร์คเท่าไหร่ เช่นตอนกรอกเครื่องหมายจุดแล้วตรวจสอบไม่ละเอียดลบได้บ้างไม่ได้บ้างเป็นต้น เลยหา lib คนอื่นมาใช้ดีกว่าเจอตัวนี้น่าสนใจดีอย่างแรกเลยก็ตอบโจทย์ที่กำลังหาพอดีเลยเอามาใช้แล้วพบว่ามีปัญหากับ vuetify เล็กน้อยเลยแงะจนมันใช้งานได้บันทึกไว้
DEMO
https://mrchoke.github.io/vuetify-cleave/
Source
Create VueJs & Veutify Project
vue create vuetify-cleave
cd vuetify-cleave
vue add vuetify
Add Cleave.js
yarn add cleave.js
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);
}
})
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>
ถ้าลอง input จะมี error ขึ้นมา แต่ถ้าใช้ input ปกติของ HTML จะไม่มีปัญหา
<input type="text" v-model="comma2" **v-cleave="{ numeral: true, numeralThousandsGroupStyle: 'thousand' }"** />
ลองค้นหาข้อมูลพบว่า 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)
}
})
TypeScript
สำหรับ TypeScript จะมีปัญหาเรื่อง properties ที่ Cleave.js แปะเข้าไปใน HTMLElement ทำให้เกิดการเตือนหรืออาจจะใช้งานไม่ได้
ขั้นแรกเพิ่ม @type/cleave.js ก่อน
yarn add -D @types/cleave.js
หลังจากนั้นให้สร้าง 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
}
แล้วแก้ในส่วนของการประกาศ 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)
}
})
ก็จะประมาณนี้ครับ
Posted on August 30, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.