Gustavo Bissolli
Gustavo Bissolli

Reputation: 1571

Vue js apply filter on v-model in an input field

Hope someone can help me! I have made a directive wrapping the Jasny Bootstrap Plugin more specifically the input-mask thing and everything goes well!

Now I have made a custom filter supported by moment to format the date field!

The date format that I receive from my backend application is YYY-MM-DD and I must show on the view as DD/MM/YYYY... I've tried v-model="date | myDate" but it didn't work properly!

JS

Vue.directive('input-mask', {
  params: ['mask'],

  bind: function() {
    $(this.el).inputmask({
      mask: this.params.mask
    });

  },
});

Vue.filter('my-date', function(value, formatString) {

  if (value != undefined)
    return '';

  if (formatString != undefined)
    return moment(value).format(formatString);

  return moment(value).format('DD/MM/YYYY');

});

var vm = new Vue({
  el: 'body',
  data: {
    date: '2015-06-26',
  }
});

HTML

<label>Date</label>
<input type="text" class="form-control" v-input-mask mask="99/99/9999" v-model="date">
<p>{{ date | myDate 'dd/mm/yyyy' }}</p>

There is the JSBin if somebody's interested!

Thanks in advance!

EDIT: Explaining better what I expect =)

When the page first load the input receive the value of 2015-06-26 and I would like to show that value as DD/MM/YYYY so 26/06/2015! It works properly only after I start typing something!

Upvotes: 31

Views: 65875

Answers (9)

use value for bind on text field example:

<v-text-field :value="string | stringFormat"></v-text-field>

Upvotes: 0

Julien J
Julien J

Reputation: 87

I would like to offer an alternative, with the hook update from Vue-directive

export default {
  data() {
    return {
      someData: '',
    }
  },
  directives: {
    lowercase: {
      update(el) {
        el.value = el.value.toLowerCase()
      },
    },
    uppercase: {
      update(el) {
        el.value = el.value.toUpperCase()
      },
    },
    capitalize: {
      update(el) {
        const lowerCase = el.value.toLowerCase()
        el.value = lowerCase.charAt(0).toUpperCase() + lowerCase.slice(1)
      },
    },
  },
}

or

Vue.directive('lowercase', {
  update(el) {
    el.value = el.value.toLowerCase()
  }
})
Vue.directive('uppercase', {
  update(el) {
    el.value = el.value.toUpperCase()
  }
})
Vue.directive('capitalize', {
  update(el) {
    const lowerCase = el.value.toLowerCase()
    el.value = lowerCase.charAt(0).toUpperCase() + lowerCase.slice(1)
  }
})
<input type="text" v-model="someData" v-capitalize />

Upvotes: 1

poimsm2
poimsm2

Reputation: 572

Template

<input type="text" v-model="date" @change="onDateChange">

Component

  methods: {
    onDateChange(event) {
      this.myDate = event.target.value
    }
    getFormatedDate(date) {
     return '2020/08/19'
    }
  },
  computed: {
    date() {
      return this.getFormatedDate(this.myDate)
    }
  }

Upvotes: 1

Samidjo
Samidjo

Reputation: 2355

Go to main.js and add the following code :

import moment from 'moment'
Vue.filter('myDate', function (value) {
    if (value) {
        return moment(String(value)).format('dd/mm/yyyy')
    }
});

In your HTML do the following :

<label>Date</label>
<v-text-field :value="date | myDate" @input="value=>date=value"></v-text-field>
<p>{{ date | myDate 'dd/mm/yyyy' }}</p>

So we used above v-bind to bind the value and @input event handler to have the v-model functionality.

Upvotes: 15

Jason Stewart
Jason Stewart

Reputation: 383

I've found that I can filter input using the ordinary component v-bind:value / v-on:input dance without resorting to data or watch clauses if I just call $forceUpdate() after emitting the filtered value:

The Vue component:

{
  props: ['value'],
  methods: {
    mEmit: function(EVT) {
      const VAL      = EVT.target.value;
      var   FILTERED = myFilterFunction(VAL);
      this.$emit('input', FILTERED);
      this.$forceUpdate();
    }
  }
}

The component HTML (data is filtered as it is entered):

  <input type="text" v-bind:value="value" v-on:input="mEmit($event)" />

Using the component:

<my-component v-model="myDataVar"></my-component>

Upvotes: 0

krchun
krchun

Reputation: 1044

This is how I implemented a vue filter for a v-model using the watch callback, this won't update the value on load.

Vue.filter('uppercase', function (value) {
    return value.toUpperCase();
});

The html:

<input type="text" v-model="someData">

And the watch callback:

watch:{
   someData(val) {
       this.someData = this.$options.filters.uppercase(val);
    },
}

Upvotes: 5

crabbly
crabbly

Reputation: 5186

I understand what you are trying to do, however, because of the two way binding when using v-model, it may be better to just format the date as you receive it from the server, and then, use it with the desired format in your front-end app ('DD/MM/YYYY').

When sending the data back to the back-end, you just format it back to the desired server format ('YYYY-MM-DD').

In your Vue app, the work flow would be something like this:

 new Vue({
    el: 'body',
    data: {
      date: null,
    },
    methods: {
        getDataFromServer: function() {
                // Ajax call to get data from server

                // Let's pretend the received date data was saved in a variable (serverDate)
                // We will hardcode it for this ex.
                var serverDate = '2015-06-26';

                // Format it and save to vue data property
                this.date = this.frontEndDateFormat(serverDate);
        },
        saveDataToServer: function() {
            // Format data first before sending it back to server
            var serverDate = this.backEndDateFormat(this.date);

            // Ajax call sending formatted data (serverDate)
        },
        frontEndDateFormat: function(date) {
            return moment(date, 'YYYY-MM-DD').format('DD/MM/YYYY');
        },
        backEndDateFormat: function(date) {
            return moment(date, 'DD/MM/YYYY').format('YYYY-MM-DD');
        }
    }
  });

This works well for me, hope it helps.

Here is a fiddle for it:

https://jsfiddle.net/crabbly/xoLwkog9/

Syntax UPDATE:

    ...
    methods: {
        getDataFromServer() {
                // Ajax call to get data from server

                // Let's pretend the received date data was saved in a variable (serverDate)
                // We will hardcode it for this ex.
                const serverDate = '2015-06-26'

                // Format it and save to vue data property
                this.date = this.frontEndDateFormat(serverDate)
        },
        saveDataToServer() {
            // Format data first before sending it back to server
            const serverDate = this.backEndDateFormat(this.date)

            // Ajax call sending formatted data (serverDate)
        },
        frontEndDateFormat(date) {
            return moment(date, 'YYYY-MM-DD').format('DD/MM/YYYY')
        },
        backEndDateFormat(date) {
            return moment(date, 'DD/MM/YYYY').format('YYYY-MM-DD')
        }
    }
  })

Upvotes: 18

james2doyle
james2doyle

Reputation: 1439

I had a similar problem when I wanted to uppercase an input value.

This is what I ended up doing:

// create a directive to transform the model value
Vue.directive('uppercase', {
  twoWay: true, // this transformation applies back to the vm
  bind: function () {
    this.handler = function () {
      this.set(this.el.value.toUpperCase());
    }.bind(this);
    this.el.addEventListener('input', this.handler);
  },
  unbind: function () {
    this.el.removeEventListener('input', this.handler);
  }
});

Then I can use this directive on the input field with a v-model.

<input type="text" v-model="someData" v-uppercase="someData">

Now, whenever I type into this field or change someData, the value is transformed to uppercase.

This essentially did the same thing as I hoped v-model="someData | uppercase" would do. But of course, you can't do that.

In summation: make a directive that transforms the data, not a filter.

Upvotes: 18

Jeff
Jeff

Reputation: 25221

When you get the value initially, adjust it to fit the input. I got it working in the ready function, but you could do this after your DB call as well:

ready: function(){    
  var year = this.date.substr(0, 4);
  var monDay = this.date.substr(5,5);
  var result = monDay + "-" + year;
  this.date = result.replace(/-/g,"/");
}

You may have to do something similar on the way back up to your database as well.

Upvotes: 2

Related Questions