Unique problem-solving for multi-select and single-select choices

My component is functioning perfectly except for the issue of binding a string instead of an array. The events are firing correctly.

In the multi-select version, when I select and deselect items, the multiSelected variable is updated properly. However, when selecting another value within the single select, the singleSelected variable remains unchanged even though the same event is triggered.

I have simplified the code below so you can easily see the logic and problem:

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>

Answer №1

v-model expects components to emit an `input` event, not directly modify the `value`.

In multi-select mode, direct mutation of the input array passed as a `value` prop might make your component function, but it renders the emitted event useless. Avoid this practice and create a new array before emitting.

In single-select mode, deselecting all checkboxes still emits a value, setting the model even when no checkbox is selected.

The design appears odd for several reasons:

  1. Using different `v-model` types (array or string) feels unnatural.
  2. In single-select mode, behaving like radio buttons can be achieved using actual radio inputs.
  3. If you switch the `multiSelect` prop at runtime, it may lead to unexpected behavior.

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>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Leverage the power of AJAX for fetching data from a database in Node.js

As I am utilizing Node.js as the server and Mysql as the database to store logged in usernames, I am curious if it is possible to use AJAX to execute a SQL database query and retrieve a list of usernames. More precisely, I am interested in using XMLHttp r ...

Using JavaScript to show a prompt message inside an h1 tag within a newly created div

I have developed a basic JavaScript program that opens a prompt dialog when the div tag is clicked, allowing the user to enter text. The program then creates a new div element and displays the entered text above it. However, I am facing an issue where I wa ...

Unlocking the power of accessing nested data in JSON files dynamically

Building a new feature that allows users to input a word, choose the language, and receive the definition along with an example using the API service. To retrieve the desired data at position 0 of "exclamation" in the "meaning" section. This ensures that ...

The function range.insertNode() is failing to insert the text node as it should

I'm currently working on developing a Chrome extension. Essentially, I want the user to be able to insert HTML format tags before and after selected text when they click on a content menu option. These tags are added in an event page that sends the f ...

The mui-datatables demo fails to display the code snippet as intended

Just diving into React and attempting to grasp MUI-datatables. The code snippet from the Codebox provided on the library's page isn't displaying in my browser, resulting in an empty page. Surprisingly, the console isn't showing any errors. ...

Trouble with updating the view when an array is modified in ng-repeat? It seems like using $scope.$apply() may not

When updating the array inside a function, the view does not automatically update. However, if you use console.log to check the array after pushing values, it shows the updated array. Even trying $scope.apply() inside $timeout did not solve this issue. Ja ...

Sending data from Web Service to Ajax Request success callback function

Hello Coding Comrades, I am currently working on a form with a SSN textbox that triggers an ajax request to determine if an employee has been hired before. The data returned from the Web Method is in the form of a TermedEmployee Object, but I'm stru ...

The challenges encountered with JSONP/Ajax Script - displaying 'Undefined'

I need help retrieving data from a webserver that I don't have control over. I've searched online but haven't found a solution yet. I've tried various codes, with and without DataTables. If anyone could provide guidance on where to go ...

Require a split dropdown feature that does not rely on bootstrap

After searching extensively for a split button dropdown field without relying on any bootstrap package, I came up empty-handed. It seems like such a specific feature is hard to find without external dependencies. https://i.sstatic.net/stoMz.png ...

Struggling with date validation as a new MomentJS user

Looking to validate a date in string format using moment JS, I am encountering an issue. Using the dd/mm/yy format in pCalendar in ngPrime, I store the date value in stDate. Here is the code I have written: var stDate = '02/02/2021'; var stDate ...

Is it possible to automatically update a select list with variable values based on the selection of another select option

Within my PHP code, I have defined an array called $programDates_items that contains various school program values, each with multiple dates assigned to them. Additionally, I have a form with a select input labeled "program of interest," which presents dif ...

Guide to verifying a Django form with AngularJs

I have a very simple form with 1 field and a submit button. I want to implement validation such that the submit button is disabled when the field is empty and enabled when it is not. I am trying to achieve this dynamically using AngularJs but seem to be mi ...

Utilize the event bus by calling `this.$root.$emit` command

I recently implemented a basic Event bus in my application to dynamically change styles on a page, and it's functioning correctly. The event bus is triggered using the $emit and $on methods as shown below: EventBus.$on and EventBus.$emit('call ...

Error: The function exec in matchExpr[type] is not defined

I made some changes to Object.prototype and now I'm running into errors with jQuery's methods on selectors. The error message I'm getting is: Uncaught TypeError: matchExpr[type].exec is not a function Additionally, when trying to use $.po ...

Unable to build a React Native library

For the past month, I've been diligently working on a react native component library, and I believe it's finally in a state where I can publish it as a package on npm. After pushing it to GitHub and running npm publish, everything seemed to be ru ...

Having trouble with the Bootstrap dropdown not activating the click event?

My issue involves a Bootstrap dropdown where the click event is not triggering. I'm curious about why the click event won't trigger and if there's a solution available to fix this problem. <div class="dropdown d-inline-block"> ...

Issue with loading a Mongoose model

I'm facing a strange problem with NodeJS, require function, and mongoose. I have defined a user model schema as follows: let mongoose = require('mongoose'); let Schema = mongoose.Schema; let depositSchema = new Schema({ customer: Stri ...

Tips for inserting HTML into elements using Angular

Recently, I delved into Angular and decided to experiment with Ajax by fetching a document to display on my webpage. The process worked flawlessly, but now I face a new challenge: injecting HTML content into a DOM element dynamically. Typically, this task ...

Storing form datepicker data into MongoDB using Node.js

Having an issue with the date formatting between Angular Material 6's Datepicker and Node.js. When displaying the date in the browser, it shows as 06/20/1992, but in the console output, it appears as Sat Jun 20 1992 00:00:00 GMT+0800 (Philippine Stand ...

What is the best way to incorporate this HTML and script into a React component?

After receiving the embedded HTML and script from a platform, I discovered that a button triggers the script. It was originally designed to be embedded on a website, but I am attempting to integrate it into a React application. Here is the code provided fo ...