3
\$\begingroup\$

I'm building a date range selector, and while this works, I feel like I'm making it more complicated than it needs to be. Is there a more elegant way of writing this?

Possibly using computed values or watches?

<template>
<form>
    <select  @change="rangeSelection">           
            <option 
                v-for="(option, key) in rangeOptions"
                :key="key"
                :value="key">
                {{option.display}}
            </option>
    </select>
    </form>
</template>

<script>
import moment from 'moment';

export default {
  name: 'DateRangeChooser',
  data: () => {
    return {
        selectedRange: "last7Days",
        startDate: moment().subtract(8, 'days'),
        endDate: moment().subtract(1, 'days'),
        rangeOptions: {
            last7Days: {
                display: 'Last 7 Days',
                startDate: moment().subtract(8, 'days'),
                endDate: moment().subtract(1, 'days')
            },
            lastWeek: {
                display: 'Last Week',
                startDate: moment().startOf('week').subtract(1, 'week'),
                endDate: moment().endOf('week').subtract(1, 'week')
            },
            last30days: {
                display: 'Last 30 days',
                startDate: moment().subtract(31, 'days'),
                endDate: moment().subtract(1, 'days')
            }
        }
    }
  },  
  methods: {
    rangeSelection: function(e){
        this.endDate = this.rangeOptions[e.currentTarget.value].endDate;
        this.startDate = this.rangeOptions[e.currentTarget.value].startDate;
    }
  }
};
</script>
\$\endgroup\$
  • \$\begingroup\$ I would suggest using date-fns instead of moment, since moment requires you to embed the entire library, and date-fns lets you import only the parts you need \$\endgroup\$ – Daniel Feb 28 at 16:35
2
\$\begingroup\$

Yes you can use computed properties:

computed: {
    endDate: function() {
      //determine endDate based on value of selectedRange
    },
    startDate: function() {
      //determine startDate based on value of selectedRange
    }
}

And v-model can be used to bind the value of the select list to that data property:

<select v-model="selectedRange">

Then use that property in the computed properties:

  computed: {
    endDate: function() {
      return this.rangeOptions[this.selectedRange].endDate;
    },
    startDate: function() {
      return this.rangeOptions[this.selectedRange].startDate;
    }
  }

With this approach there is no need to define the rangeSelection method and bind it to the onchange property.

rangeOptions could also be moved outside the component, and then the first key could be used to select the default value of selectedRange instead of hard coding it. See the snippet below.

const rangeOptions = {
  last7Days: {
    display: 'Last 7 Days',
    startDate: moment().subtract(8, 'days'),
    endDate: moment().subtract(1, 'days')
  },
  lastWeek: {
    display: 'Last Week',
    startDate: moment().startOf('week').subtract(1, 'week'),
    endDate: moment().endOf('week').subtract(1, 'week')
  },
  last30days: {
    display: 'Last 30 days',
    startDate: moment().subtract(31, 'days'),
    endDate: moment().subtract(1, 'days')
  }
};
const form = new Vue({
  el: '#DateRangeChooser',
  data: () => {
    return {
      selectedRange: Object.keys(rangeOptions)[0],
      rangeOptions
    }
  },
  computed: {
    endDate: function() {
      return rangeOptions[this.selectedRange].endDate;
    },
    startDate: function() {
      return rangeOptions[this.selectedRange].startDate;
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<div id="DateRangeChooser">
  <select v-model="selectedRange">
    <option v-for="(option, key) in rangeOptions" :key="key" :value="key">
      {{option.display}}
    </option>
  </select>
  <div>Start: {{startDate.format('LL')}}</div>
  <div>End: {{endDate.format('LL')}}</div>
</div>

Edit

@blindman67 brought up an interesting point:

Sorry my VUE knowledge is limited but I thought computed properties were cached. Could be a problem in some edge cases, eg time near midnight and user selects, then changes their mind waits till after midnight selects again?1

That is true:

... computed properties are cached based on their dependencies2

if caching is an issue, then you may need to look at using a watcher or just calculate the computed property each time instead of referencing values within this.rangeOptions.

1Vue component over-complication

2https://vuejs.org/v2/guide/computed.html#Computed-Caching-vs-Methods

\$\endgroup\$
  • 1
    \$\begingroup\$ Sorry my VUE knowledge is limited but I thought computed properties were cached. Could be a problem in some edge cases, eg time near midnight and user selects, then changes their mind waits till after midnight selects again? \$\endgroup\$ – Blindman67 Oct 24 '18 at 20:04
  • \$\begingroup\$ Good point - I amended my answer \$\endgroup\$ – Sᴀᴍ Onᴇᴌᴀ Oct 24 '18 at 22:08
  • \$\begingroup\$ Thank you both, this certainly leads me in the right direction! \$\endgroup\$ – mad Oct 31 '18 at 17:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.