I need to use Vuetify v-date-picker
in different ponents. But that will lead to code duplication. So I thought it will be interesting to create a custom <custom-date-picker />
ponent which I can use anywhere I need to.
- This child ponent should emit to the parent the value of the formatted date.
- The parent ponent has a button which console logs the formatted date
But with my current code, I am getting this error message:
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent ponent re-renders. Instead, use a data or puted property based on the prop's value. Prop being mutated: "value"
found in
---> <CustomDatePicker> at ponents/CustomDatePicker.vue
<Pages/index.vue> at pages/index.vue
Parent ponent is pages/index.vue:
<template>
<div>
<custom-date-picker v-model="date" />
<v-btn @click="getDate">
Ok
</v-btn>
</div>
</template>
<script>
import CustomDatePicker from '@/ponents/CustomDatePicker.vue'
export default {
ponents: { CustomDatePicker },
data () {
return {
date: ''
}
},
methods: {
getDate () {
console.log(this.date)
}
}
}
</script>
Child ponent is ponents/CustomDatePicker.vue:
<template>
<v-container fill-height>
<v-row justify="center" align="center">
<v-col cols="12">
<!-- Date picker -->
<v-menu
ref="menu1"
v-model="menu1"
:close-on-content-click="false"
transition="scale-transition"
offset-y
>
<template v-slot:activator="{ on }">
<v-text-field
v-bind:value="value"
v-on:input="$emit('input', $event)"
@blur="date = parseDate(value)"
v-on="on"
value
label="Date"
color="green lighten-1"
/>
</template>
<v-date-picker
v-model="date"
@input="menu1 = false"
no-title
header-color="green lighten-1"
color="green lighten-1"
/>
</v-menu>
<!-- end of date picker -->
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
name: 'CustomDatePicker',
props: {
value: {
type: String,
default: ''
}
},
data () {
return {
menu1: null,
date: null
}
},
puted: {
putedDateFormatted () {
return this.formatDate(this.date)
}
},
watch: {
date (val) {
this.value = this.formatDate(this.date)
}
},
methods: {
formatDate (date) {
if (!date) { return null }
return date
},
parseDate (date) {
if (!date) { return null }
const [year, month, day] = date.split('-')
return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`
}
}
}
</script>
How to overe my issue ?
This simple demo is available on Github if you have time for it :)
UPDATE 1:
I succeeded to get rid of the error message above by avoiding mutating the prop value
. I can choose the date and when I click on the Ok button I get the date console logged properly.
The issue is that the text field of the parent ponent does not show the date I picked, it stays as it is shown on the picture above.
Here is the updated code of the child ponent:
<template>
<v-container fill-height>
<v-row justify="center" align="center">
<v-col cols="12">
<!-- Date picker -->
<v-menu
ref="menu1"
v-model="menu1"
:close-on-content-click="false"
transition="scale-transition"
offset-y
>
<template v-slot:activator="{ on }">
<v-text-field
v-model="dateFormatted"
@blur="date = parseDate(dateFormatted)"
v-on="on"
value
label="Date"
color="green lighten-1"
/>
</template>
<v-date-picker
v-bind:value="value"
v-on:input="$emit('input', $event)"
@input="menu1 = false"
no-title
header-color="green lighten-1"
color="green lighten-1"
/>
</v-menu>
<!-- end of date picker -->
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
name: 'CustomDatePicker',
props: {
value: {
type: String,
default: ''
}
},
data () {
return {
menu1: null,
date: null,
dateFormatted: null
}
},
puted: {
putedDateFormatted () {
return this.formatDate(this.date)
}
},
watch: {
date (val) {
this.dateFormatted = this.formatDate(this.date)
}
},
methods: {
formatDate (date) {
if (!date) { return null }
return date
},
parseDate (date) {
if (!date) { return null }
const [year, month, day] = date.split('-')
return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`
}
}
}
</script>
I need to use Vuetify v-date-picker
in different ponents. But that will lead to code duplication. So I thought it will be interesting to create a custom <custom-date-picker />
ponent which I can use anywhere I need to.
- This child ponent should emit to the parent the value of the formatted date.
- The parent ponent has a button which console logs the formatted date
But with my current code, I am getting this error message:
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent ponent re-renders. Instead, use a data or puted property based on the prop's value. Prop being mutated: "value"
found in
---> <CustomDatePicker> at ponents/CustomDatePicker.vue
<Pages/index.vue> at pages/index.vue
Parent ponent is pages/index.vue:
<template>
<div>
<custom-date-picker v-model="date" />
<v-btn @click="getDate">
Ok
</v-btn>
</div>
</template>
<script>
import CustomDatePicker from '@/ponents/CustomDatePicker.vue'
export default {
ponents: { CustomDatePicker },
data () {
return {
date: ''
}
},
methods: {
getDate () {
console.log(this.date)
}
}
}
</script>
Child ponent is ponents/CustomDatePicker.vue:
<template>
<v-container fill-height>
<v-row justify="center" align="center">
<v-col cols="12">
<!-- Date picker -->
<v-menu
ref="menu1"
v-model="menu1"
:close-on-content-click="false"
transition="scale-transition"
offset-y
>
<template v-slot:activator="{ on }">
<v-text-field
v-bind:value="value"
v-on:input="$emit('input', $event)"
@blur="date = parseDate(value)"
v-on="on"
value
label="Date"
color="green lighten-1"
/>
</template>
<v-date-picker
v-model="date"
@input="menu1 = false"
no-title
header-color="green lighten-1"
color="green lighten-1"
/>
</v-menu>
<!-- end of date picker -->
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
name: 'CustomDatePicker',
props: {
value: {
type: String,
default: ''
}
},
data () {
return {
menu1: null,
date: null
}
},
puted: {
putedDateFormatted () {
return this.formatDate(this.date)
}
},
watch: {
date (val) {
this.value = this.formatDate(this.date)
}
},
methods: {
formatDate (date) {
if (!date) { return null }
return date
},
parseDate (date) {
if (!date) { return null }
const [year, month, day] = date.split('-')
return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`
}
}
}
</script>
How to overe my issue ?
This simple demo is available on Github if you have time for it :)
UPDATE 1:
I succeeded to get rid of the error message above by avoiding mutating the prop value
. I can choose the date and when I click on the Ok button I get the date console logged properly.
The issue is that the text field of the parent ponent does not show the date I picked, it stays as it is shown on the picture above.
Here is the updated code of the child ponent:
<template>
<v-container fill-height>
<v-row justify="center" align="center">
<v-col cols="12">
<!-- Date picker -->
<v-menu
ref="menu1"
v-model="menu1"
:close-on-content-click="false"
transition="scale-transition"
offset-y
>
<template v-slot:activator="{ on }">
<v-text-field
v-model="dateFormatted"
@blur="date = parseDate(dateFormatted)"
v-on="on"
value
label="Date"
color="green lighten-1"
/>
</template>
<v-date-picker
v-bind:value="value"
v-on:input="$emit('input', $event)"
@input="menu1 = false"
no-title
header-color="green lighten-1"
color="green lighten-1"
/>
</v-menu>
<!-- end of date picker -->
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
name: 'CustomDatePicker',
props: {
value: {
type: String,
default: ''
}
},
data () {
return {
menu1: null,
date: null,
dateFormatted: null
}
},
puted: {
putedDateFormatted () {
return this.formatDate(this.date)
}
},
watch: {
date (val) {
this.dateFormatted = this.formatDate(this.date)
}
},
methods: {
formatDate (date) {
if (!date) { return null }
return date
},
parseDate (date) {
if (!date) { return null }
const [year, month, day] = date.split('-')
return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`
}
}
}
</script>
Share
Improve this question
edited Dec 19, 2019 at 7:41
Billal BEGUERADJ
asked Dec 19, 2019 at 6:42
Billal BEGUERADJBillal BEGUERADJ
22.8k45 gold badges123 silver badges140 bronze badges
5
- How to overe my issue ... I'd suggest you Avoid mutating a prop directly and Instead, use a data or puted property based on the prop's value – Jaromanda X Commented Dec 19, 2019 at 7:23
- Thank you, that is exactly what I avoided now (after I posted the question), I get the chosen date when I clock on the button, but it is not reflected on the text field for the moment (it stays as it is on the picture) @JaromandaX – Billal BEGUERADJ Commented Dec 19, 2019 at 7:34
- Did you post the new code @JaromandaX ? – Jesper Commented Dec 19, 2019 at 7:38
- I updated my post with relevant code and explanation. @Djip – Billal BEGUERADJ Commented Dec 19, 2019 at 7:42
- I updated my question with half working code @JaromandaX – Billal BEGUERADJ Commented Dec 19, 2019 at 7:43
1 Answer
Reset to default 6Changes i've made to get it to work, is that i've added a puted
with a get()
and set()
. The getter will return the current selected value, and the setter will be emitting the new value each time it's changing.
This is a good way of utilizing the v-model
within custom ponents.
CustomDatePicker.vue
<template>
<v-container fill-height>
<v-row justify="center" align="center">
<v-col cols="12">
<!-- Date picker -->
<v-menu
ref="menu1"
v-model="menu1"
:close-on-content-click="false"
transition="scale-transition"
offset-y
>
<template v-slot:activator="{ on }">
<v-text-field
v-model="selected"
v-on:input="$emit('input', $event)"
@blur="date = parseDate(value)"
v-on="on"
value
label="Date"
color="green lighten-1"
/>
</template>
<v-date-picker
v-model="selected"
@input="menu1 = false"
no-title
header-color="green lighten-1"
color="green lighten-1"
/>
</v-menu>
<!-- end of date picker -->
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
name: 'CustomDatePicker',
props: {
value: {
type: String,
default: ''
}
},
data () {
return {
menu1: null,
date: null
}
},
puted: {
selected: {
get() {
return this.value
},
set(value) {
this.$emit('input', value)
}
},
putedDateFormatted () {
return this.formatDate(this.date)
}
},
watch: {
date (val) {
this.value = this.formatDate(this.date)
}
},
methods: {
formatDate (date) {
if (!date) { return null }
return date
},
parseDate (date) {
if (!date) { return null }
const [year, month, day] = date.split('-')
return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`
}
}
}
</script>