Search code examples
vue.jsvuejs2datepickervuetify.js

v-date-picker allow write date with keyboard


I´m trying to do that my v-date-picker allow that my users can write date with keyboard and select date with date-picker. I´m reading documentation, i´m showing examples... but i can´t find solution. I think that should be very simple but i don´t know how i can to do this. I hope that anybody can help me.

I have this component, FormDatePicker.vue that all content it´s:

<template>
    <v-flex>
        <strong>{{ label }}</strong>
        <v-menu
            ref="dateMenu"
            v-model="dateMenu"
            :close-on-content-click="false"
            :nudge-right="40"
            transition="scale-transition"
            offset-y
            max-width="290px"
            min-width="290px"
        >
            <template v-slot:activator="{ on }">
                <v-text-field
                    slot="activator"
                    v-model="valorDateFormatted"
                    persistent-hint
                    append-outer-icon="event"
                    clearable
                    solo
                    v-on="on"
                    :error-messages="errorMessages"
                    @click:clear="limpiarDate()"
                ></v-text-field>
            </template>
            <v-date-picker
                locale="es-ES"
                v-on="$listeners"
                v-model="currentValue"
                no-title
                first-day-of-week=1
                @input="dateChanged()"
                :min="min"
                :max="max"
                :disabled="desactivar"
                @change="useSelectedData"
            ></v-date-picker>
        </v-menu>
    </v-flex>
</template>

<script>
    export default {
        inheritAttrs: false,
        data: () => ({
            dateMenu: false,
        }),
        props: {
            errorMessages:  { type: Array, default: null},
            label:          { type: String, default: ''},
            value:          { default: ''},                     //Nota: Elimino el type: String porque provoca un error cuando le paso números.
            min: { type: String, default: null },
            max: { type: String, default: null },
            desactivar: { type: Boolean, default: null}
        },
        computed: {
            inputListeners: function () {
                var vm = this
                // `Object.assign` merges objects together to form a new object
                return Object.assign({},
                    // We add all the listeners from the parent
                    this.$listeners,
                    // Then we can add custom listeners or override the
                    // behavior of some listeners.
                    {
                        // This ensures that the component works with v-model
                        input: function (event) {
                            vm.$emit('input', event.target.value)
                        }
                    }
                )
            },
            valorDateFormatted: {
                get : function () {
                    return this.formatDate(this.value);
                },
                set: function(value) {
                    return this.formatDate(value);
                }
            },
            currentValue: {
                get: function() {
                    if (this.value != null) {
                        return this.value.split(' ')[0];
                    }
                    return this.value;
                },
                set: function(val) {
                    this.$emit('input', val);
                }
            }
        },
        methods: {
            dateChanged() {
                this.dateMenu = false;
            },
            formatDate (date) {
                if (!date) return null

                const [year, month, day] = date.split(' ')[0].split('-')
                return `${day}/${month}/${year}`
            },

            limpiarDate(){
                this.$emit('input', null);
            },
        }
    }
</script>

<style scoped>
    .theme--light.v-input >>> input, .theme--light.v-input >>> textarea{
        margin-top: 10px;
    }
</style>

I tryed add @blur and function tu blur, i tryed add @focus and function to focus, but not run.

Thank your very much for read me and sorry for my bad english


Solution

  • I read Shkirkov's answer and I think it can be done simpler.

    What we want is to bind 2 values in the v-text-field v-model: the date value by the date picker and the one written with the keyboard. For that, we have to define the getter and setter in the computed variable and we CAN NOT put "readonly" as an attribute for v-date-picker.

    Your template will look like this:

    <template>
      <v-menu
        v-model="menu"
        :close-on-content-click="false"
        transition="scale-transition"
        offset-y
        max-width="290px"
        min-width="290px"
      >
        <template #activator="{ on, attrs }">
          <v-text-field
            v-model="formattedDate"
            v-bind="attrs"
            v-on="on"
            ref="datePicker"
          />
        </template>
        <v-date-picker
          color="secondary"
          locale="ru-RU"
          no-title
          v-model="internalDate"
          @input="onDatePickerValueChanged"
          :first-day-of-week="1"
          :max="max"
        />
      </v-menu>
    </template>
    

    And as for your script:

    <script>
    export default {
      name: 'x-date-picker',
      data() {
        return {
          menu: false,
          internalDate: null¿
        }
      },
      computed: {
        /**
         * v-date-picker uses format YYYY-MM-DD, but out local format is DD.MM.YYYY
         */
        formattedDate: {
          get() {
            return (this.internalDateValue && this.internalDateValue.length === 10)
              ? this.$moment(this.internalDate).format('DD.MM.YYYY')
              : this.internalDateValue
          },
          set(newValue) {
             if(newValue && newValue.length==10){
                 let newDate = `${newValue.substring(6, 10)}-${newValue.substring(3, 5)}-${newValue.substring(0, 2)}`
                 this.internalDate = newDate
          }
        }
    

    We need to pass to internalDate the date in the format YYYY-MM-DD because is the date picker's format!

    Hope it works for you :)