Looking to implement v-model with a group of checkboxes in a Custom Component in Vue3?

The code snippet below demonstrates the power of v-model. By checking and unchecking checkboxes, the checkedNames array will automatically add or remove names. No need to manually manipulate the array with push, slice, or filter operations.

const { ref } = Vue;

const App = {
  setup () {
    const checkedNames = ref([])
    return { checkedNames }
  }
}

Vue.createApp(App).mount("#app");
<script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="98eeedfdd8abb6a8b6a9a9">[email protected]</a>/dist/vue.global.js"></script>

<div id="app">
  <input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
  <label for="jack">Jack</label>
  <input type="checkbox" id="john" value="John" v-model="checkedNames">
  <label for="john">John</label>
  <input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
  <label for="mike">Mike</label>
  <br>
  <span>Checked names: {{ checkedNames }}</span>
</div>

I'm wondering how can I achieve a similar feature using custom components?

You'll notice in the following code snippets that I attempted to replicate this functionality but felt it lacked the magic of v-model. Instead, I had to handle checkbox logic within my function addOrRemoveItem().

Just a heads-up: It might seem like I'm over-explaining, apologies for that.

I referenced this tutorial for some guidance, which wasn't very helpful. Then, I consulted the Vue official documentation here. Below is the functional code, but it seems a bit excessive.

const { ref, createApp } = Vue;

const app = createApp({
  setup() {
    const itemsSelected = ref([]);
    const items = ref([
      { id: '1', name: 'Jack' }, 
      { id: '2', name: 'John' }, 
      { id: '3', name: 'Mike' }, 
    ]);

    const addOrRemoveItem = (itemId) => {
      const exists = itemsSelected.value.includes(itemId);

      if (exists) {
        itemsSelected.value = itemsSelected.value.filter((id) => id !== itemId);
      } else {
        itemsSelected.value.push(itemId);
      }
    };

    return { 
      items,
      itemsSelected,
      addOrRemoveItem,
    };
  },
});

app.component('custom-checkbox', {
   props: {
    item: { type: Object, default: () => ({}) },
    modelValue: { type: Array, default: () => [] },
  },
  template: `
    <div>
      <input
        type="checkbox" :value="item.id"
        @change="$emit('update:model-value', $event.target.checked)"
      > {{ item.name }}
    </div>
    `
})

app.mount("#app");
<script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="8cfaf9e9ccbfa2bca2bdbd">[email protected]</a>/dist/vue.global.js"></script>

<div id="app">
  <div><code>itemsSelected: {{ itemsSelected }}</code></div>
 <hr />
  <custom-checkbox
    v-for="item in items"
    :key="item.id"
    :item="item"
    :model-value="itemsSelected"
    @update:model-value="addOrRemoveItem(item.id)"
  ></custom-checkbox>
</div>

As mentioned earlier, the code seemed cumbersome when following the instructions from the Vue documentation $emit('update:model-value', ...), where it can be simplified to something like $emit('val-updated'). Here's a streamlined version after removing unnecessary props and shortening the $emit event.

const { ref, createApp } = Vue;

const app = createApp({
  setup() {
    const itemsSelected = ref([]);
    const items = ref([
      { id: '1', name: 'Jack' }, 
      { id: '2', name: 'John' }, 
      { id: '3', name: 'Mike' }, 
    ]);

    const addOrRemoveItem = (itemId) => {
      const exists = itemsSelected.value.includes(itemId);

      if (exists) {
        itemsSelected.value = itemsSelected.value.filter((id) => id !== itemId);
      } else {
        itemsSelected.value.push(itemId);
      }
    };

    return { 
      items,
      itemsSelected,
      addOrRemoveItem,
    };
  },
});

app.component('custom-checkbox', {
   props: {
    item: { type: Object, default: () => ({}) },
  },
  template: `
    <div>
      <input
        type="checkbox" :value="item.id"
        @change="$emit('val-updated')"
      > {{ item.name }}
    </div>
    `
})

app.mount("#app");
<script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="91e7e4f4d1a2bfa1bfa0a0">[email protected]</a>/dist/vue.global.js"></script>

<div id="app">
  <div><code>itemsSelected: {{ itemsSelected }}</code></div>
 <hr />
  <custom-checkbox
    v-for="item in items"
    :key="item.id"
    :item="item"
    @val-updated="addOrRemoveItem(item.id)"
  ></custom-checkbox>
</div>

Answer №1

It is unnecessary to use the addOrRemoveItem() method in this case. Instead, rely on the default Vue functionality for a checkbox with the v-model. The key is to utilize a computed property for the v-model, as direct mutation of props from a child component is not allowed.

const {
  ref,
  createApp
} = Vue;

const app = createApp({
  setup() {
    const itemsSelected = ref([]);
    const items = ref([{
        id: '1',
        name: 'Jack'
      },
      {
        id: '2',
        name: 'John'
      },
      {
        id: '3',
        name: 'Mike'
      },
    ]);

    return {
      items,
      itemsSelected,
    };
  },
});

app.component('custom-checkbox', {
  props: {
    item: {
      type: Object,
      default: () => ({})
    },
    modelValue: {
      type: Array,
      required: true
    }
  },
  emits: ['update:modelValue'],
  computed: {
    model: {
      get() {
        return this.modelValue
      },
      set(value) {
        this.$emit('update:modelValue', value)
      }
    }
  },
  template: `
    <div>
      <input
        type="checkbox" :value="item.id" v-model="model"
      > {{ item.name }}
    </div>
    `
})

app.mount("#app");
<script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="ddaba8b89deef3edf3ecec">[email protected]</a>/dist/vue.global.js"></script>

<div id="app">
  <div><code>itemsSelected: {{ itemsSelected }}</code></div>
  <hr />
  <custom-checkbox v-for="item in items" :key="item.id" :item="item" v-model="itemsSelected"></custom-checkbox>
</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

Struggling with grasping the concept of promises in AngularJS

I encountered a challenge while working with AngularJS & REST. My data service was not returning data in time for my model to use, despite implementing a promise. I would greatly appreciate any assistance from those more knowledgeable on this matter. In ...

Problem encountered with a selection menu

I'm experiencing some trouble with the dropdown menu. Whenever I try to move the mouse to access the dropdown menu, it seems unresponsive. I attempted adjusting the padding on a line of code but ended up moving the entire line instead. Here is the cod ...

Utilize a singular object to contain multiple instances of the useState hook

const [regionData, setRegionData] = useState({ country: "", city: "", population: "", location: "", temp_min: "" }); Does anyone know a more efficient and cleaner way to replace these individual useState hooks by organizing them into ...

Press the button using the spacebar

I am facing an issue where I have a button with an anchor element that I need to trigger with the spacebar key for accessibility purposes. However, instead of triggering the button, pressing the spacebar causes the page to jump down when the button is in f ...

Looking to display an element right away, like a loading spinner, in Vue? If nextTick isn't doing the trick, here's what you can try

Imagine having a Vue component stored in a .vue file with a data member called isLoading: false. The template includes: <div v-show="isLoading" id="hey" ref="hey">Loading...</div> <button @click="loadIt()">Load it</button> Along w ...

What is the procedure for a parent component to transmit HTML to a child component within Angular?

How can a parent component pass multiple ng-templates to a child component? For example, the parent component includes multiple ng-templates: <app-childcomponent> <ng-template>Item A</ng-template> <ng-template>Item B</n ...

Utilize VueJS to bind a flat array to a v-model through the selection of multiple checkboxes

My Vue component includes checkboxes that have an array of items as their value: <div v-for="group in groups"> <input type="checkbox" v-model="selected" :value="group"> <template v-for="item in group"> <input type ...

A fixed header for tables that shifts along with horizontal scrolling

Seeking assistance with a persistent issue - I have a table with a fixed header on vertical scroll (see CSS and Javascript below), but the header fails to move horizontally when I scroll. window.onscroll = functi ...

creating dynamic navigation tabs with scroll functionality

Utilizing Bootstrap Nav Tabs and Tab panes on my website has been a smooth process. However, I am encountering some difficulty in adding extra links that not only activate the Tab Panes but also scroll to them. The issue seems to be related to a specific l ...

A tutorial on making a POST request using axios in a React application

I am struggling with my JavaScript skills I need assistance with calling the following CURL command successfully: curl -X POST -H "Content-Type: application/json" -u john.doe:moqui -d "{\"firstName\":\"Hung&bso ...

Is it possible to include array elements in a dropdown menu using React?

Imagine you have an array called const places = [" a1", "a2", "a3"]; and <FormControl variant="outlined" className={classes.formControl}> <InputLabel id="dropdown_label">Testing</InputL ...

Trouble arises when managing click events within the Material UI Menu component

I've implemented the Menu Component from Material UI as shown below - <Menu open={open} id={id} onClose={handleClose} onClick={handleClick} anchorEl={anchorEl} transformOrigin={{ horizontal: transformOriginRight, vertical: t ...

Received an error while attempting an AJAX GET request to a distinct server hosting a WebAPI

This is my first time encountering an issue with an Ajax request in client-side code. I'm hoping that there's a simple mistake in my code that I'm just overlooking. Just to give some background, when I manually access the URL mentioned below ...

Exploring the possibilities with Rollbar and TypeScript

Struggling with Rollbar and TypeScript, their documentation is about as clear as AWS's. I'm in the process of creating a reusable package based on Rollbar, utilizing the latest TS version (currently 4.2.4). Let's delve into some code snipp ...

App for registering for an AngularJS course

Currently, I am developing an application that involves a JSON list containing information about various courses such as title, course ID, location, and type. One of the features in my app includes a modal window that appears when a user clicks on a speci ...

Matching regex only on complete strings, not on parts of strings

My goal is to dynamically add and remove strings to a textarea when values in a table are clicked. The functionality should allow users to select and deselect values in the table, with the selected values adding or removing themselves from the textarea. It ...

Position the typography component to the right side

Is there a way to align two typography components on the same line, with one aligned to the left and the other to the right? I'm currently using this code but the components are aligned next to each other on the left side. const customStyles = makeSt ...

Displaying success and failure messages on a modal window using Ajax in PHP form

Unable to display error messages or success messages in the same modal window. I have set up Wordpress and using the following code: Form <form id="formcotizador" method="post" action="<?php echo get_template_directory_uri(); ?>/cotizador/procc ...

Ways to verify if a user is authenticated without relying on request.session

I am currently developing a web application using Express, Docker, and following a Three-layered architecture. In my app, I store user login information in a session and have blogposts as a key resource. To retrieve the blogpostId from the database in the ...

The variable process.env.CLIENT_ID is functioning correctly within a function, but does not work when declared as a global

Trying to implement oAuth for Google API Using .env file to hide sensitive variables with .gitignore Facing an issue when trying to define the CLIENT_ID variable globally CLIENT_ID = process.env.CLIENT_ID When I run and log the variable outside of a fun ...