I'm new to Vue and have spent the past several hours looking into how to use conditional logic within methods and computed properties for a practice project creating a birthday component. I've seen others use if/else statements in their computed property, but am getting an Unexpected side effect in "displayDays" computed property
error message.
My objective is to build this component to return a DOB string in the mm/dd/yyyy
format when selecting each individual DOB element through a list of options (or in this case, buttons). When a user clicks on the birthday month, I'd like the list of days in that month to then display the appropriate array of dates in the template associated to that month.
And that's where I'm stuck. I originally tried to add in the array values on the method click event selectedMonth()
and after some research, realized I needed to use computed properties to change the data within the days array like I wanted. But, after multiple attempts at trying to have an if/else statement run without errors into the computed section, I'm not sure what I'm missing.
Any help or direction on this is greatly appreciated!
Below is my code:
<template>
<div>
<div id="months">
<label >Month</label>
<label>Select a month</label>
<div>
<button
v-for="month in months"
:key="month.text"
:value="month.value"
v-on:click="selectedMonth(month)"
>{{ month.text }}</button>
</div>
</div>
<div id="days">
<label >Day</label>
<label>Select a day</label>
<div>
<button
v-for="day in displayDays"
:key="day"
v-on:click="selectedDay(day)"
>{{ day }}</button>
</div>
</div>
<div id="year">
<label >Input Year</label>
<label aria-label="Four Digit Year">Four Digit YYYY</label>
<input
type="text"
id="dobYear"
name="year"
maxlength="4"
>
</div>
<button>
Submit DOB
</button>
</div>
</template>
<script>
export default {
name: 'birthday',
data() {
return {
months: [
{text: 'Jan', value: '01'},
{text: 'Feb', value: '02'},
{text: 'Mar', value: '03'},
{text: 'Apr', value: '04'},
{text: 'May', value: '05'},
{text: 'Jun', value: '06'},
{text: 'Jul', value: '07'},
{text: 'Aug', value: '08'},
{text: 'Sep', value: '09'},
{text: 'Oct', value: '10'},
{text: 'Nov', value: '11'},
{text: 'Dec', value: '11'}
],
days: [],
year: '',
active: true,
birthday:['','','',],
}
},
methods: {
selectedMonth(month) {
// console.log('clicked ' month);
this.birthday[0] = month.value;
this.birthday[1] = '';
console.log(this.birthday);
},
selectedDay(day) {
// console.log('clicked ' day);
this.birthday[1] = day;
console.log(this.birthday);
this.birthDay = day;
},
submittedYear(year) {
this.birthday[2] = year;
console.log(this.birthday);
}
},
computed: {
displayDays() {
if(this.month === 'Feb') {
return this.days = ['01','02','03','04','05','06','07','08','09','10',
'11','12','13','14','15','16','17','18','19','20',
'21','22','23','24','25','26','27','28', '29']
} else if(this.month === 'Apr' || this.month === 'Jun' || this.month === 'Sep' || this.month === 'Nov') {
return this.days = ['01','02','03','04','05','06','07','08','09','10',
'11','12','13','14','15','16','17','18','19','20',
'21','22','23','24','25','26','27','28','29','30']
} else {
return this.days = ['01','02','03','04','05','06','07','08','09','10',
'11','12','13','14','15','16','17','18','19','20',
'21','22','23','24','25','26','27','28','29','30','31']
}
}
}
}
</script>
<style scoped>
#months, #days, #year {
border: 0.125rem solid #e5e5e5;
border-radius: 0.5rem;
margin-bottom: 0.625rem;
}
#months > div, #days > div, #year {
display: flex;
flex-wrap: wrap;
flex-direction: row;
gap: 0.75rem 0.4375rem;
justify-content: center;
padding: 10% 11% 10%;
box-sizing: border-box;
}
button {
text-align: center;
width: 5.0625rem;
height: 2.625rem;
background: #fff;
border-radius: 6.25rem;
border: 0.125rem solid #5500B3;
line-height: 2.4rem;
}
#days > div {
justify-content: left;
}
#days > div > button {
border-radius: 10rem;
width: 2.625rem;
}
#months > div > button:hover, #days > div > button:hover {
cursor: pointer;
background: #efefef;
}
#year input {
font-size: 1.125rem;
font-weight: 400;
border: #ececec;
box-sizing: border-box;
padding: 0.3125rem 0.875rem 0rem;
background: #ececec solid;
border-radius: 0.625rem;
height: 3.75rem;
border-style: none;
}
.uppercase {
text-transform: uppercase;
}
</style>
CodePudding user response:
The issue is with the computed property part.
You should not simply assign this.days
with the required array inside the displayDays
computed method. Instead you have to return the required array from the computed property.
Also, computer properties will be re evaluated when any on of the paremter inside the computed property changes. Here you are using this.month
inside the computed property, but you are not updating this anywhere inside your code so your computed property will never be re evaluated. So you have to assign the value for this.month
inside selectedMonth
method as this.month = month.text;
. Once the value of this.month
changes, your computed property will be evaluated again with the new value for this.month
.
Working Fiddle
new Vue({
el: '#app',
name: 'birthday',
data() {
return {
months: [
{ text: 'Jan', value: '01' },
{ text: 'Feb', value: '02' },
{ text: 'Mar', value: '03' },
{ text: 'Apr', value: '04' },
{ text: 'May', value: '05' },
{ text: 'Jun', value: '06' },
{ text: 'Jul', value: '07' },
{ text: 'Aug', value: '08' },
{ text: 'Sep', value: '09' },
{ text: 'Oct', value: '10' },
{ text: 'Nov', value: '11' },
{ text: 'Dec', value: '11' }
],
days: [],
year: '',
active: true,
birthday: ['', '', '',],
month: null,
}
},
methods: {
selectedMonth(month) {
// console.log('clicked ' month);
this.birthday[0] = month.value;
this.birthday[1] = '';
console.log(this.birthday);
this.month = month.text;
},
selectedDay(day) {
// console.log('clicked ' day);
this.birthday[1] = day;
console.log(this.birthday);
this.birthDay = day;
},
submittedYear(year) {
this.birthday[2] = year;
console.log(this.birthday);
}
},
computed: {
displayDays() {
if (this.month === 'Feb') {
return ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10',
'11', '12', '13', '14', '15', '16', '17', '18', '19', '20',
'21', '22', '23', '24', '25', '26', '27', '28', '29']
} else if (this.month === 'Apr' || this.month === 'Jun' || this.month === 'Sep' || this.month === 'Nov') {
return ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10',
'11', '12', '13', '14', '15', '16', '17', '18', '19', '20',
'21', '22', '23', '24', '25', '26', '27', '28', '29', '30']
} else {
return ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10',
'11', '12', '13', '14', '15', '16', '17', '18', '19', '20',
'21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31']
}
}
}
})
#months, #days, #year {
border: 0.125rem solid #e5e5e5;
border-radius: 0.5rem;
margin-bottom: 0.625rem;
}
#months > div, #days > div, #year {
display: flex;
flex-wrap: wrap;
flex-direction: row;
gap: 0.75rem 0.4375rem;
justify-content: center;
padding: 10% 11% 10%;
box-sizing: border-box;
}
button {
text-align: center;
width: 5.0625rem;
height: 2.625rem;
background: #fff;
border-radius: 6.25rem;
border: 0.125rem solid #5500B3;
line-height: 2.4rem;
}
#days > div {
justify-content: left;
}
#days > div > button {
border-radius: 10rem;
width: 2.625rem;
}
#months > div > button:hover, #days > div > button:hover {
cursor: pointer;
background: #efefef;
}
#year input {
font-size: 1.125rem;
font-weight: 400;
border: #ececec;
box-sizing: border-box;
padding: 0.3125rem 0.875rem 0rem;
background: #ececec solid;
border-radius: 0.625rem;
height: 3.75rem;
border-style: none;
}
.uppercase {
text-transform: uppercase;
}
<script src="https://cdn.jsdelivr.net/npm/vue@^2"></script>
<div id="app">
<div>
<div id="months">
<label >Month</label>
<label>Select a month</label>
<div>
<button v-for="month in months" :key="month.text" :value="month.value"
v-on:click="selectedMonth(month)">{{ month.text }}</button>
</div>
</div>
<div id="days">
<label >Day</label>
<label>Select a day</label>
<div>
<button v-for="day in displayDays" :key="day" v-on:click="selectedDay(day)">{{ day
}}</button>
</div>
</div>
<div id="year">
<label >Input Year</label>
<label aria-label="Four Digit Year">Four Digit YYYY</label>
<input type="text" id="dobYear" name="year" maxlength="4">
</div>
<button>
Submit DOB
</button>
</div>
</div>