【问题标题】:Custom multi-select and single select issue自定义多选和单选问题
【发布时间】:2022-01-07 13:15:26
【问题描述】:

我创建了一个组件,除了绑定字符串而不是数组之外,它可以完美运行。事件正确触发。

所以在多选版本中,如果我选择和取消选择项目,multiSelected 变量会正确更新。但是,当在单个选择中选择另一个值时,singleSelected 变量不会被更改,但是会触发相同的事件。

在这里简化代码,以便您可以看到逻辑和问题:

Vue.component('dropdown', {
  template: `<div class="dropdown">
    <label v-for="item in items" :key="item.value" :class="{selected:selected.indexOf(item.value) > -1}">
      {{ item.label }}
      <input type="checkbox" :value="item.value" :checked="selected.indexOf(item.value) > -1" @change="selected = $event" />
    </label>
  </div>`,
  props: [ 'value', 'items', 'multiSelect' ],
  computed: {
    selected: {
      get: function() {
        if (this.value === undefined) {
          return [];
        }
        if (!Array.isArray(this.value)) {
          return [ this.value ];
        }
        return this.value;
      },
      set: function($event) {
        let current = this.selected;

        if (!this.multiSelect) {
          current = $event.target.value;
        }

        if (this.multiSelect && !$event.target.checked) {
          const index = current.indexOf($event.target.value);
          if (index > -1) {
            current.splice(index, 1)
          }
        }
        if (this.multiSelect && $event.target.checked) {
          current.push($event.target.value);
        }

        console.log(current);
        this.$emit('value', current);
      }
    }
  }
});

Vue.component('wrapper', {
  template: `
    <div>
      Single
      <dropdown :items="items" v-model="singleSelected" :multi-select="false" name="single" />
      <br />
      Multi
      <dropdown :items="items" v-model="multiSelected" :multi-select="true" name="multi" />
      <p>Models</p>
      <p>singleSelected: {{ singleSelected }}</p>
      <p>multiSelected: {{ multiSelected }}</p>
    </div>
  `,
  data() {
    return {
      items: [{value:'bmw',label:'BMW',count:1},{value:'audi',label:'Audi',count:1},{value:'kia',label:'KIA',count:1}],
      multiSelected: ['kia'],
      singleSelected: 'kia',
    }
  }
});

new Vue().$mount('#app');
.dropdown {
  border: 1px solid black;
  padding: 10px;
  display: block;
}
label {
  margin: 5px;
}


.selected {
  color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <wrapper>
    
  </wrapper>
</div>

【问题讨论】:

  • 你应该使用一个数组 - current = [$event.target.value];

标签: javascript vue.js vuejs2


【解决方案1】:

v-model 在自定义组件 expects 上,该组件将发出 input 事件,而不是 value

您的组件(在某种程度上)在多选模式下工作的唯一原因是因为您直接改变了由 value 属性传递给您的输入数组(换句话说,发出的事件是无用的)。不要那样做。在发射之前创建一个新数组

此外,单选模式还有一个问题 - 仅取消选中复选框后,您仍会发出值,因此即使未选中任何复选框,模型也会设置为一个值

我个人觉得你的设计很奇怪有几个原因:

  1. 使用不同的v-model“类型”(数组或字符串)感觉不自然
  2. 在单选模式下,它就像收音机一样,所以为什么不使用收音机
  3. 如果在运行时切换 multiSelect 属性会发生什么?

Vue.component('dropdown', {
  template: `<div class="dropdown">
    <label v-for="item in items" :key="item.value" :class="{selected:selected.indexOf(item.value) > -1}">
      {{ item.label }}
      <input type="checkbox" :value="item.value" :checked="selected.indexOf(item.value) > -1" @change="selected = $event" />
    </label>
  </div>`,
  props: [ 'value', 'items', 'multiSelect' ],
  computed: {
    selected: {
      get: function() {
        if (this.value === undefined) {
          return [];
        }
        if (!Array.isArray(this.value)) {
          return [ this.value ];
        }
        return this.value;
      },
      set: function($event) {
        if(this.multiSelect) {
          if (!$event.target.checked) {
            this.$emit('input', this.selected.filter(v => v !== $event.target.value))
          } else {
            this.$emit('input', [...this.selected, $event.target.value])
          }
        } else {
          this.$emit('input', $event.target.checked ? $event.target.value : "")
        }
      }
    }
  }
});

Vue.component('wrapper', {
  template: `
    <div>
      Single
      <dropdown :items="items" v-model="singleSelected" :multi-select="false" name="single" />
      <br />
      Multi
      <dropdown :items="items" v-model="multiSelected" :multi-select="true" name="multi" />
      <p>Models</p>
      <p>singleSelected: {{ singleSelected }}</p>
      <p>multiSelected: {{ multiSelected }}</p>
    </div>
  `,
  data() {
    return {
      items: [{value:'bmw',label:'BMW',count:1},{value:'audi',label:'Audi',count:1},{value:'kia',label:'KIA',count:1}],
      multiSelected: ['kia'],
      singleSelected: 'kia',
    }
  }
});

new Vue().$mount('#app');
.dropdown {
  border: 1px solid black;
  padding: 10px;
  display: block;
}
label {
  margin: 5px;
}


.selected {
  color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <wrapper>
    
  </wrapper>
</div>

【讨论】:

  • 谢谢,这是我的一个愚蠢的错误,我应该仔细检查我发出的事件。
【解决方案2】:

您可以等待事件和触发方法:

Vue.component('dropdown', {
  template: `<div class="dropdown">
    <label v-for="item in items" :key="item.value" :class="{selected:selected.indexOf(item.value) > -1}">
      {{ item.label }}
      <input type="checkbox" :value="item.value" :checked="selected.indexOf(item.value) > -1" @change="selected = $event" />
    </label>
  </div>`,
  props: [ 'value', 'items', 'multiSelect' ],
  computed: {
    selected: {
      get: function() {
        if (this.value === undefined) {
          return [];
        }
        if (!Array.isArray(this.value)) {
          return [ this.value ];
        }
        return this.value;
      },
      set: function($event) {
        let current = this.selected;

        if (!this.multiSelect) {
          current = $event.target.value;
        }

        if (this.multiSelect && !$event.target.checked) {
          const index = current.indexOf($event.target.value);
          if (index > -1) {
            current.splice(index, 1)
          }
        }
        if (this.multiSelect && $event.target.checked) {
          current.push($event.target.value);
        }

        console.log(current);
        this.$emit('value', current);
      }
    }
  }
});

Vue.component('wrapper', {
  template: `
    <div>
      Single
      <dropdown :items="items" v-model="singleSelected" :multi-select="false" name="single" @value="updateSingle" />
      <br />
      Multi
      <dropdown :items="items" v-model="multiSelected" :multi-select="true" name="multi"  />
      <p>Models</p>
      <p>singleSelected: {{ singleSelected }}</p>
      <p>multiSelected: {{ multiSelected }}</p>
    </div>
  `,
  data() {
    return {
      items: [{value:'bmw',label:'BMW',count:1},{value:'audi',label:'Audi',count:1},{value:'kia',label:'KIA',count:1}],
      multiSelected: ['kia'],
      singleSelected: 'kia',
    }
  },
  methods: {
    updateSingle(val) {
      this.singleSelected = val
    }
  }
});

new Vue().$mount('#app');
.dropdown {
  border: 1px solid black;
  padding: 10px;
  display: block;
}
label {
  margin: 5px;
}


.selected {
  color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <wrapper>
    
  </wrapper>
</div>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-26
    相关资源
    最近更新 更多