1
votes

I'm new with Vue, so I trying to create a Input Date Picker using Vuetify.

This is my code:

Codepen

The problem is, when I select a date in the picker, I get an error in console:

[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: '$value'

Is that the correct way to create that component?

1

1 Answers

3
votes

As the error states, you cannot change a prop from a child. This is because Vue uses this parent - child flow: enter image description here

With this structure, you assure that the parent's data is the only source of truth. In order to change data, you need to emit it up to the parent. You almost had it in your example. Instead of emitting a change event, you need to emit input for v-model to sync the changes.

This is more clear when you see the example from the docs:

<input v-model="searchText">

Is indeed the same as:

<input
  v-bind:value="searchText"
  v-on:input="searchText = $event.target.value"
>

However, since vue 2.3.0, you can now do changes to props via the .sync modifier

Vue.component('date-picker', {
    props: ['label'],
    data: () => ({
        date: null,
        dateFormatted: null,
        open: false
    }),
    created() {
        this.date = this.$value;
    },
    methods: {
        formatDate (date) {
            if (!date) return null
            const [year, month, day] = date.split('-')
            return `${day}/${month}/${year}`
        },
        parseDate (date) {
            if (!date) return null
            const [day, month, year] = date.split('/')
            return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`
        },
        update() {
            this.$emit('input', this.dateFormatted);
        }
    },
    watch: {
        date (val) {
            this.dateFormatted = this.formatDate(this.date)
        },
        dateFormatted(val) {
            this.update();
        }
    },
    template:`
        <v-menu
            ref="open"
            :close-on-content-click="false"
            v-model="open"
            :nudge-right="40"
            lazy
            transition="scale-transition"
            offset-y
            full-width
            >
            <v-text-field
                slot="activator"
                v-model="dateFormatted"
                :label="label"
                placeholder="dia/mês/ano"
                @blur="date = parseDate(dateFormatted)"
            ></v-text-field>
            <v-date-picker v-model="date" no-title @input="open = false"></v-date-picker>
        </v-menu>
    `
});

Vue.config.productionTip = false;
Vue.config.devtools=false

new Vue({
  el: '#app',
  data: () => ({
    myDate: '2018-09-01'
  })
});
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuetify/1.2.0/vuetify.min.js"></script>


<div id="app">
  <v-app>
     <v-flex xs24>
       <date-picker label="Select a date" v-model="myDate"></date-picker>
       <br>
       Current date: {{myDate}}
    </v-flex>
  </v-app>
</div>