Dynamically Enable / Disable Select Options

I have an array of
days=[]
used in a select

['Sunday', 'Monday', .....]

and an array of
hours=[]
at 30 min intervals like so:

{
    "disabled": false,
    "value": 0,
    "display": "12:00 AM"
  },
  {
    "disabled": false,
    "value": 1,
    "display": "12:30 AM"
  },
  {
    "disabled": false,
    "value": 2,
    "display": "1:00 AM"
  },

and so on for the full 24 hours

Now what I need to do is mark hours disabled if they have been selected for that day, and also prevent overlapping hours specific to that day.

Why you ask? Think of a restaurant hours. Most are open say 9am - 10pm easy enough.
But a fancy restaurant might be open
Monday 8am - 10am for breakfast,
Monday 11am - 1pm for lunch
Monday 7pm - 10pm for Supper

So I need a way to disable the hours for monday that have been selected and also prevent overlap.
Overlap I mean if using the Monday example above you could not select 7am - 12:00pm because it would overlap hours already in use. Another way to think of it would be physically being at a place, you can not be at two places at the same time.

I plan to use the hour “value” key since it matches the array key so each set of times from start to end will Using Monday breakfast hour values 16 and 20 so i need a way to set disabled: true for keys 16,17,18,19,20

Then if they want to add another entry for the same day it must take into account the disabled/used hours both and start and end either before 16 or after 20

Any ideas to get started?

Thanks,

Dave

You could define the disabled attribute dynamically in the vue template:

<select>
  <option v-for="opt in options" :key="opt.value" :disabled="opt.disabled">{{opt.display}}</option>
</select>

This is my select:

<v-select
    ref="formData.start"
    v-model="formData.start"
    :items="hours" // i need to mark the items in the hours array disabled: true
    label="Start"
    item-value="value"
    item-text="time"
    dense
    clearable>
  </v-select>

Its a matter of how do I mark for example hours[16] thru hours[20] disabled: true, and this need to be changed / updated on the select change of days. If they select say Tuesday and no hours have been selected previously then all hours are available, but if they select say Monday which has hour keys 16-20 already used the hours array now needs to reflect that.

If it’s v-select from vuetify, there is item-disabled props as well. Is it working?

What I am asking is how to change the values of the array.

{
    "disabled": false, // change
    "value": 0,
    "display": "12:00 AM"
  },
  {
    "disabled": false, // change
    "value": 1,
    "display": "12:30 AM"
  },
  {
    "disabled": false, // change
    "value": 2,
    "display": "1:00 AM"
  },

The hours array is keyed 0-47 so I need a simple efficient way to on demand change based on what has already been selected. Monday for example has 0-12 and 24-30 as 2 sets of hours selected. so I need to update the hours[keys 0,1,2,3,…12 and 24,25,26,27,…30].disabled = true when the "select a day " select input is Monday, if Tuesday selected do the same function to update all hours selected for Tuesday…and so on and and on.

Not how to disable the select option, that I know, its how to change the array which in-turn will make the options disabled when the hours array is changed.

I see, sorry for misunderstood your problem.

IMO, I would create the array of these values as a computed value that will map the expected set of the hours with the correct disabled flag.

For ex:

data () {
  return {
    day: 'monday',
    // define your rules of enabled hours here based on each day
    // Object.freeze is meant to make this value not reactive, as it's just a static value
    rules: Object.freeze({
      'monday': [0,1,2],
      'tuesday': [1,2],
      // and the rest of the days..
    }),
    // Object.freeze is meant to make this value not reactive, as it's just a static value
    hours: Object.freeze([
      {value: 0, display: "12:00 AM"},
      {value: 1, display: "12:30 AM"},
      {value: 2, display: "1:00 AM"},
      // and so on...
    ])
  }
},
computed: {
  // use this computed hoursOption for the select options
  hoursOption () {
    const rule = this.rules[this.day];
    return this.hours.map(hour => ({
      ...hour,
      // if the value matched the rule, do not disable it
      disabled: rule.indexOf(hour.value) >= 0 ? false : true
    }))
  }
}

Because the array of option is a computed value that watches the this.day, so whenever the day has changed, the option value should react properly to disable the hours based on the rules given.

Sorry I haven’t tested the script above, but I hope you get the idea. Let me know if it works for you.

Thanks,

2 issue I see though is:

rules: Object.freeze({
      'monday': [0,1,2],
      'tuesday': [1,2],
      // and the rest of the days..
    }),

as the array is not static. A form is used for the user to select day (Sun - Sat) and then start hour and end hour. So rules needs to be computed since the [day]: array is created from the records the user has saved. So I will need to come up with a computed / watcher so when the user selects a day i create from any records for Monday like you have ‘monday’: [0,1,2], on the fly

And second is the hours array is computed / generated. I am not manually creating the array of 48 keys.

hours() {
  const x = 30
  const times = []
  let tt = 0
  const ap = [' AM', ' PM']

  for (let i = 0; tt < 24 * 60; i++) {
    const hh = Math.floor(tt / 60)
    const mm = tt % 60
    let time =
      ('' + (hh === 12 ? 12 : hh % 12)).slice(-2) +
      ':' +
      ('0' + mm).slice(-2) +
      ap[Math.floor(hh / 12)]

    times.push({
      disabled: false,
      value: i,
      display: time
    })

    tt = tt + x
  }

  return times
},

I will give it a try and let you know how it turns out!

Much appreciated.

Dave

1 Like

Cool @movepixels. My examples above of making it as static values was just an assumption of myself that the rules are static. Yes, you need to come up with yourself on how to generate your rules of the hours by each days.

Nice function to generate the hours! As long as the value returned are accessible to the computed function, then it should be fine.

Yes, hope it works. Thanks!

Thanks,

Will reply with solution or issues along the way.

Right now the users records are returned / grouped / keyed by day to match $moments day (0 = Sunday, 1 = Monday) and so on

So plan is when user selects a day I get the value (0 thru 6) and pull the records for the matching day to build the array of whats been used and then use your hoursOption () function to make whats available.

Thanks again for the help to get me going!

Dave

1 Like

Ok i have it working with a few modifications.

First I have an “range” that’s coming from the server pre-modified and saved in Vuex which is simply an array of hours already selected / saved to database with key being the value of the day select:

range:
0: Array(9) // key 0 matches the select value of Sunday
0: 7
1: 8
2: 9
3: 0
4: 1
5: 2
6: 3
7: 4
8: 5
2: Array(4) // key 2 matches the select value of Tuesday
0: 44
1: 45
2: 46
3: 47

When a user select a day the computed function then marks the values from range array as disabled for the hours to select from, and if no range then return all the hours with nothing disabled

Thanks to chenxeed

hoursOption() {
      if (this.range[this.form.day]) {
        const rule = this.range[this.formData.day]
        return this.hours.map(hour => ({
          ...hour,
          disabled: rule.indexOf(hour.value) >= 0
        }))
      }
      return this.hours
    },

Next will be validation to prevent overlapping hours.

Glad it works!