没有重复值的多个下拉列表

Posted

技术标签:

【中文标题】没有重复值的多个下拉列表【英文标题】:Multiple dropdowns with no duplicate value 【发布时间】:2021-11-11 11:53:13 【问题描述】:

我正在尝试复制一个 Vue 组件,它是一个项目列表,每个项目都包含一个下拉列表和一个删除按钮。将有一个“添加”按钮将新项目添加到列表中,如下面的 sn-p 所示。

要求是,当用户选择一个选项时,该选项对于任何其他项目都将不可用(或删除)。换句话说,选择的选项值不应该重复。这与这个问题中的想法非常相似(jQuery Prevent duplicate choice in multiple dropdowns)

当用户重新选择或删除一个项目时,附加到它的选定选项应再次添加到“可用”列表中。因此,选项列表是“反应性的”和动态的。

例如,对于第一项,如果我选择“选项 1”。单击“添加新项目”时,“选项 1”不应在选项列表中。如果第一个项目被删除,“选项 1”将再次可供选择,等等。 . .

这是我目前得到的,想法是option 将存储所有选项数据,selectedValueArray 将负责存储每个项目的选定选项值,selectableOptions 数组将等于 options 设置减去selectedValueArray。通过与 item 交互(更改选项、删除),selectedValueArrayselectableOptions 数组将相应更改。

我可以用 javascript 做到这一点。但是我是 Vue 的新手,不知道如何在 Vue 中有效地做到这一点。我创建的 sn-p 的问题是,由于可用的选项来自selectableOptions 数组,所以当从selectableOptions 中删除一个项目时,它也会影响选定的选项。 (例如:如果从该数组中删除“选项 1”,则第一项中的下拉菜单将为空白,因为“选项 1”已从可选列表中删除)。任何帮助表示赞赏。

        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>

【问题讨论】:

【参考方案1】:

改进的答案,选择disabled 选项的&lt;select&gt; 元素将不会被提交。请改用v-show

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);

    ,
    
   isShown: 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-show="isShown(option)">option.name</option>
    </select>
    <button @click="removeItem(index)">Remove this item</button>
  </div>
  <button @click="addItem" v-show="items.length<options.length">Add new item</button>
</div>

【讨论】:

【参考方案2】:

如果您想简单地禁用其值存在于 items 对象数组中的选项(您将其用于 v-model 指令绑定,因此它反映了一组“实时”用户选择的选项) ,那么就是使用方法返回禁用状态了:

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

然后,您可以定义一个 isDisabled(option) 方法,该方法返回一个布尔值以指示给定的 option 的值是否已存在于您的数组中:

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

请参阅下面的示例证明:

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>

【讨论】:

这种方法对我来说看起来不错,但不会使逻辑过于复杂 我试图在使用this.items.map(item =&gt; item.value).length &lt; options.length 完全选择所有选项时隐藏添加按钮,这种方法可以实现吗? 这不行,&lt;select&gt; 选择了disabled 选项的元素将不会被提交【参考方案3】:

您必须使用计算属性来过滤 selectableOptions

类似的东西


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

【讨论】:

嗨 Cosimo,按照最常识,我应该将这个计算得到的 computedSelectable 属性添加到 Vue 实例中,并在选项 for 循环中将 selectedValueArray 替换为 computedSelectable。但是这个计算属性似乎只“运行”一次而不是反应性的。也许缺少什么?

以上是关于没有重复值的多个下拉列表的主要内容,如果未能解决你的问题,请参考以下文章

如何获取下拉列表的选定值的文本? [复制]

Yii2 gridview过滤来自多个值的列表(不是下拉列表过滤器)

从具有不同值的逗号分隔 JSON 对象填充 html 下拉列表

使用JS动态创建国家下拉列表[重复]

[使用多个选择下拉列表过滤HTML表

在文件选择器中禁用下拉列表[重复]