Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleave is broken with Vue #459

Open
ArtyomBist opened this issue Apr 4, 2019 · 8 comments
Open

Cleave is broken with Vue #459

ArtyomBist opened this issue Apr 4, 2019 · 8 comments

Comments

@ArtyomBist
Copy link

Cleave is not working with vue/vuetify
vuetifyjs/vuetify#6910 (comment)

@fatihacet
Copy link

fatihacet commented Apr 23, 2019

There is really nothing wrong with Cleave or Vuetify. Here is my v-text-field wrapper component which works perfectly with Cleave.

<script>
import Cleave from 'cleave.js';

export default {
  props: {
    value: {
      type: [String, Number],
      required: false,
      default: '',
    },
  },
  data() {
    return {
      currentValue: this.value,
    };
  },
  mounted() {
    this.cleave = new Cleave(this.$el.querySelector('input'), {
      numeral: true,
      onValueChanged: this.onValueChanged.bind(this),
    });

    this.cleave.setRawValue(this.value);
  },
  methods: {
    onValueChanged({ target }) {
      this.$nextTick(() => {
        this.currentValue = target.value;
        this.$emit('input', target.rawValue);
      });
    },
  },
};
</script>

<template>
  <v-text-field
    v-model="currentValue"
    v-bind="$attrs"
    :label="$t(domain)"
    height="22"
    class="caption rbx-text-input"
  />
</template>

I would like to mention two specific lines here.

this.$nextTick(() => { ... } in onValueChanged method

This is the actual trick to prevent entering non-numeric characters to target input. Also, to be able to show Cleave formatted value properly in a Vuetify textarea, you have to use a $nextTick. Without this formatting will be wrong.


this.cleave.setRawValue(this.value); in the mounted hook

This will set the initial value you got from your server so that Cleave can format it properly. Without this line, if you use custom delimiter and numeralDecimalMark in Cleave options, Cleave won't be able to show correctly formatted value.

@ankurk91
Copy link
Contributor

@aeharding
Copy link

For those that are still having issues binding Cleave to this.$refs.input (or whatever) -- I found a surprisingly easy fix that hasn't been documented yet:

v-model.lazy="foo"

The .lazy modifier makes Vue wait for Cleave to validate by binding to change events (instead of input events).

@paolog22
Copy link

paolog22 commented Dec 30, 2019

@fatihacet thanks this works for me.. for other people reference :)
but i was looking to make this as directive much easier to apply and maintain i guess ?

`<template>
    <v-text-field v-model="currentValue" v-bind="$attrs"></v-text-field>
</template>

<script lang="ts">
import { Component, Vue, Prop, Watch } from 'vue-property-decorator'
import Cleave from 'cleave.js'
import { CleaveOptions } from 'cleave.js/options'

@Component
export default class VTextFormat extends Vue {
    @Prop() value!: any
    @Prop(Object) readonly option!: CleaveOptions
    @Prop(Boolean) readonly raw!: boolean

    cleave: Cleave = null
    currentValue = this.value

    mounted () {
        const input = this.$el.querySelector('input')
        this.cleave = new Cleave(input, {
            ...this.option,
            onValueChanged: this.onValueChanged.bind(this)
        })

        this.cleave.setRawValue(this.value)
    }

    onValueChanged ({ target }) {
        this.$nextTick(() => {
            let value = this.raw ? target.rawValue : target.value
            this.currentValue = target.value
            this.$emit('input', value)
        })
    }

    beforeDestroy () {
        if (this.cleave) {
            this.cleave.destroy()
            this.cleave = null
        }
    }
}
</script>`

@harlet
Copy link

harlet commented Apr 4, 2020

Or as directive (Vuetify), it is hackish but works for now.

Vue.directive('cleave', {
  bind(el, binding, vnode) {
    const input = el.querySelector('input')
    // @ts-ignore
    input._vCleave = new Cleave(input, binding.value)
  },
  update: (el) => {
    const input = el.querySelector('input')
    const event = new Event('input', {bubbles: true});
    setTimeout(function () {
      input.value = input._vCleave.properties.result
      input.dispatchEvent(event)
    }, 100);
  },
  unbind(el) {
    const input = el.querySelector('input')
    // @ts-ignore
    input._vCleave.destroy()
  }
})

@gutisalex
Copy link

@harlet That works good but keep in mind that you cannot fill form data with a click anymore. The data shows up for a second and disappears. But for now its a decent workaround to the issue. Just wondering if this is gonna get properly fixed any time?!

@Francosf7
Copy link

For those that are still having issues binding Cleave to this.$refs.input (or whatever) -- I found a surprisingly easy fix that hasn't been documented yet:

v-model.lazy="foo"

The .lazy modifier makes Vue wait for Cleave to validate by binding to change events (instead of input events).

This worked. Thanks!

@nicolaskopp
Copy link

Vue 2.6 did introduce a probably breaking change by using MicroTasks instead of MacroTasks, see

vuejs/vue#8450 (comment)

for the problem and a solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants