Home > Blockchain >  Multiple dropdowns with no duplicate value
Multiple dropdowns with no duplicate value

Time:09-17

I'm trying to replicate a Vue component, which is a list of items, each item contains a dropdown and a remove button. There will be an "Add" button that add new item to the list, which is depicted in the snippet below.

The requirement is that when ever user select an option, that option will not be available (or remove) for any other item. In other words, selected option values should not be duplicated. Which is quite similar to the idea in this question (jQuery Prevent duplicate choice in multiple dropdowns)

When user re-select or remove an item, the selected option attached to it should be added again to "available" list. The option list is therefore "reactive" and dynamic.

For example, for the first item, if I select "Option 1". "Option 1" should not be in option list when "Add new item" is clicked. And if first item is removed, "Option 1" will be available for select again, etc,. . .

This is what I got so far, the idea is that option will store all option data, selectedValueArray will responsible for storing selected option value for each item, and selectableOptions array will equal to options set minus selectedValueArray. By interacting with item (changing option, remove), selectedValueArray and selectableOptions array will be changed accordingly.

I can do this with JavaScript. However I'm new to Vue and not sure how to do it in Vue effectively. The problem of the snippet I created is that because of available options comes from selectableOptions array, so when an item is removed from selectableOptions, it will also affect selected option. (e.g: If "Option 1" is removed from this array, the dropdown in the first item will be blank because "Option 1" has already been removed from selectable list). Any help is appreciated.

        var app = new Vue({

            el: "#app",
            data: {
                options: [],
                items: [],
                selectableOptions: [],
                selectedValueArray: [],

            },
            mounted() {
            this.options = [
                    {
                        name: "Option 1",
                        value: 1,
                    },
                    {
                        name: "Option 2",
                        value: 2,
                    },
                    {
                        name: "Option 3",
                        value: 3,
                    },
                    {
                        name: "Option 4",
                        value: 4,
                    },
                    {
                        name: "Option 5",
                        value: 5,
                    },
                    {
                        name: "Option 6",
                        value: 6,
                    }
            ];
                this.selectableOptions = this.options;
            },

            methods: {
                addItem: function () {
                    this.items.push({
                    'value': 0
                    });

                },

                removeItem: function (index) {
                    this.$delete(this.items, index);
                    
                },

                changeOption: function () {
                    this.selectedValueArray = [];
                    for (let i = 0; i < this.items.length; i  ) {
                        let selectedValue = this.items[i].value;
                        this.selectedValueArray.push(selectedValue);
                    }
                
                    this.selectableOptions = this.options.filter(                   
                                            option => { 
                        return this.selectedValueArray.indexOf(option.value) == -1;
                      })                 

                },

            },

        })
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
   <div v-for="(item, index) in items">
      <select 
        v-model="item.value">
        <option v-for="(option) in selectableOptions" :value="option.value">{{option.name}}</option>
      </select>
      <button @click="removeItem(index)">Remove this item</button>
   </div>
   <button @click="addItem">Add new item</button>
</div>

CodePudding user response:

If you want to simply disable an option whose value is present in the items array of objects (which you are using for the v-model directive binding, so it reflects a "live" set of user-selected choices), then it is a matter of using a method to return a disabled state:

<option v-for="(option) in options" :value="option.value" v-bind:disabled="isDisabled(option)">{{option.name}}</option>

Then, you can define a isDisabled(option) method which returns a boolean to indicate if a given option's value is already present in your array:

isDisabled: function(option) {
    return this.items.map(item => item.value).includes(option.value);
}

See proof-of-example below:

var app = new Vue({

  el: "#app",
  data: {
    options: [],
    items: [],
    selectedValueArray: [],

  },
  mounted() {
    this.options = [{
        name: "Option 1",
        value: 1,
      },
      {
        name: "Option 2",
        value: 2,
      },
      {
        name: "Option 3",
        value: 3,
      },
      {
        name: "Option 4",
        value: 4,
      },
      {
        name: "Option 5",
        value: 5,
      },
      {
        name: "Option 6",
        value: 6,
      }
    ];
  },

  methods: {
    addItem: function() {
      this.items.push({
        'value': 0
      });

    },

    removeItem: function(index) {
      this.$delete(this.items, index);

    },
    
    isDisabled: function(option) {
      return this.items.map(item => item.value).includes(option.value);
    }

  },

})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <div v-for="(item, index) in items">
    <select v-model="item.value">
      <option v-for="(option) in options" :value="option.value" v-bind:disabled="isDisabled(option)">{{option.name}}</option>
    </select>
    <button @click="removeItem(index)">Remove this item</button>
  </div>
  <button @click="addItem">Add new item</button>
</div>

CodePudding user response:

you have to use a computed property, that filter the selectableOptions

something like this

{
computed: {
    computedSelectable() {
      const chosenValues = this.selectedValueArray.map((i) => i.value);
      return this.selectableOptions.filter((item) =>
        !chosenValues.includes(item.value)
      );
    },
  }
}
  • Related