Reputation: 55
This works correctly in my test project but I do not know exactly how this is being done. In other words I do not understand how this is being built with Vue.js. It seems a little complicated way of doing things. I feel that this can be simplified. Any suggestions to help simply this would be great.
Basically, I want to create a list of months from 01 - 12 for a credit card expiration.
< script >
export default {
methods: {
minCardMonth() {
if (this.cardYear === this.minCardYear) return new Date().getMonth() + 1;
return 1;
}
}
} <
/script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<select class="input-control">
<option disabled selected>-- Month --</option>
<option v-bind:value="n < 10 ? '0' + n : n" v-for="n in 12" v-bind:disabled="n < minCardMonth" v-bind:key="n">
{{n
< 10 ? '0' + n : n}} </option>
</select>
Upvotes: 0
Views: 1349
Reputation: 8329
Here's a snippet that's a bit more advanced: it also handles the edge case when a user selects a future year and a month that's already past this year, then changes back to the current year (the month select defaults to the original value).
The whole point is: sometimes it seems easy to control the UI (<template>
parts) of a component - but it's always better to decouple the visuals from the data. Try organizing your code so that the presentation/UI reacts to changes in the underlying data, and control the data.
In this case, you may be right, maybe this snippet is a bit overkill - but to be honest, I wouldn't do it otherwise for myself (maybe add TypeScript :) )
// generating two digits months numbers
const MONTHS_OF_YEAR = () => {
return Array(12)
.fill(0)
.map((_, i) => {
return {
value: i,
text: ("0" + (i + 1)).slice(-2),
disabled: false,
}
})
}
// the default (unselectable) value in
// the months options:
const DEFAULT_MONTH_VALUE = () => ({
text: "--- Month ---",
value: null,
disabled: true,
})
const DEFAULT_YEAR_VALUE = () => [
"--- Year ---",
2021,
2022,
2023,
2024,
2025,
]
new Vue({
el: "#app",
data() {
return {
cardYear: DEFAULT_YEAR_VALUE()[0],
cardMonth: DEFAULT_MONTH_VALUE(),
cardYearOptions: DEFAULT_YEAR_VALUE(),
cardMonthOptions: MONTHS_OF_YEAR(),
}
},
computed: {
minCardYear() {
return new Date().getFullYear()
},
minCardMonth() {
return new Date().getMonth()
},
possibleMonths() {
// defaulting to "all months are available"
let returnMonths = [...this.cardMonthOptions]
if (this.cardYear === this.minCardYear) {
returnMonths = this.cardMonthOptions
.map((month, i) => {
if (i < this.minCardMonth) {
return {
...month,
disabled: true,
}
}
return month
})
}
// returning the array that will
// be iterated over - its first
// value is always the default
return [
DEFAULT_MONTH_VALUE(),
...returnMonths,
]
},
},
watch: {
// if cardYear is changed from a future date
// to the current year, it's wise to check
// selected month - maybe we're past that month
cardYear(newVal) {
const selectedMonth = this.possibleMonths.find(month => {
return month.value === this.cardMonth.value
})
if (selectedMonth?.disabled) {
this.cardMonth = DEFAULT_MONTH_VALUE()
}
},
},
template: `
<div>
MinCardYear: {{ minCardYear }}<br />
SelectedCardYear: {{ cardYear }}<br />
SelectedCardMonth: {{ cardMonth.text }}<br />
<select v-model="cardYear">
<option
v-for="(year, i) in cardYearOptions"
:key="year"
:value="year"
:disabled="i === 0"
>
{{ year }}
</option>
</select>
<select v-model="cardMonth">
<option
v-for="month in possibleMonths"
:key="month.value"
:value="month"
:disabled="month.disabled"
>
{{ month.text }}
</option>
</select>
</div>
`
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>
Upvotes: 1