Vuetify and Cleave.js
Tags: cleavejs, javascript, vuetify, textformatting
Published: 2020-08-30 09:15:09

---
title: Vuetify and Cleave.js
published: true
date: 2020-08-30 09:10:37 UTC
tags: cleavejs,javascript,vuetify,textformatting
canonical_url: 
---

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

#### DEMO

[https://mrchoke.github.io/vuetify-cleave/](https://mrchoke.github.io/vuetify-cleave/)

[vuetify-cleave](https://mrchoke.github.io/vuetify-cleave/)

#### Source

[mrchoke/vuetify-cleave](https://github.com/mrchoke/vuetify-cleave)

#### Create VueJs & Veutify Project

```
vue create vuetify-cleave

cd vuetify-cleave

vue add vuetify
```

#### Add Cleave.js

[nosir/cleave.js](https://github.com/nosir/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](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>
```

![](https://cdn-images-1.medium.com/max/1024/1*snZ2mWR8_PSYCMn88U7oGA.png)<figcaption>vuetify text field error</figcaption>

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

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

![](https://cdn-images-1.medium.com/max/696/1*BAa-L1D4plWvR-AlF0MFpQ.png)<figcaption>input ของ Vuetify vs Native HTML</figcaption>

ลองค้นหาข้อมูลพบว่า 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)
  }
})
```

![](https://cdn-images-1.medium.com/max/770/1*43L5Y79dGTsmu7pzVMcU5g.png)<figcaption>ทำงานได้เหมือนกันละ</figcaption>

#### 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)
  }
})
```

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

![](https://cdn-images-1.medium.com/max/1024/1*HTfNnUCcc71lcTeJ4597zg.png)<figcaption>ตัวอย่าง</figcaption>