Build a custom multi-select checkbox component bound to an array using Vue 3, Tailwind Css, and Font Awesome

Kevin Bigelow
5 min readDec 17, 2020

In response to a comment on Build a checkbox component with Vue 3, Font Awesome, & Tailwind CSS

Code Sandbox:

If you want to follow along, open up the Single Checkbox sandbox.

We’ll be taking the single checkbox component we built and utilizing it in a new multi-checkbox component.

List of options with checkboxes, options Diego, Allison, and Klaus are selected (ids 2,3,4)
Here’s the component we’ll be building

Step 1

Create a new component and import it into App.vue

Name the component multi-checkbox.vue — it can be empty for now 😶

<template></template>
<script></script>

In App.vue import the multi-checkbox component, register it, and add it to your template in place of <checkbox …/> from the last article.

Step 2 • Prepare the data

Add data and options in App.vue

For our multi-checkbox component to be reusable, it should receive its options and value as props. So let’s define our options and value in App.vue so we can pass them down to our component.

To make it a little more interesting — let’s pretend we’re building an app that lets you select heroes that you’d like on your team.

  1. Add a const called heroes and set its value as an empty Array. This is what we’ll tie the v-model to.
  2. The options should be an Array of Objects. Each option should have an id and a name for display purposes. The id is what we will populate our heroes array with as the user checks each box. (Hope you’ve watched The Umbrella Academy)

Here is how your App.vue script should look at this point.

<script>
import MultiCheckbox from "./components/multi-checkbox.vue";
import { ref } from "vue";
setup() {
const heroes = ref([]);
const options = ref([
{ name: "Luther", id: 1 },
{ name: "Diego", id: 2 },
{ name: "Allison", id: 3 },
{ name: "Klaus", id: 4 },
{ name: "Five", id: 5 },
{ name: "Ben", id: 6 },
{ name: "Vanya", id: 7 },
]);
return {
heroes,
options,
};
},
components: {
"multi-check-box": MultiCheckbox,
},
</script>

Tip: Read-up on reactivity https://vuejs.org/api/reactivity-core.html

Step 3 • Pass the data with props

One more thing in App.vue – in the template, pass options and heroes to <multi-check-box/> as props. Add v-model: right before the value prop to utilize form input binding.

<multi-check-box v-model:value=”heroes” :options=”options” />

Ok, lets get back to the empty multi-checkbox.vue component we made in step 1, we still need to add prop definitions to accept value and options

Add the prop definitions

Open up multi-checkbox.vue and add required props for options and value with type set as Array for each.

Bonus points: Ensure each option has a name and an id by writing a validator. 🧐

<script>
export default {
props: {
value: {
type: Array,
required: true,
},
options: {
type: Array,
required: true,
validator: (value) => {
const hasNameKey = value.every((option) =>
Object.keys(option).includes("name")
);
const hasIdKey = value.every((option) =>
Object.keys(option).includes("id")
);
return hasNameKey && hasIdKey;
},
},
},
</script>

More on .every(), Object.keys(), and .includes()

Step 4 • Render each option

Import and register the checkbox component as check-box

Throw in the checkbox with a v-for="option in options"

I wrapped the list of checkboxes in a div with a few tailwind classes to add some style.

<template>
<div class="flex flex-col items-start justify-center w-64 border-2 border-gray-400 p-8 rounded-lg bg-gray-100">
<check-box
v-for="option in options"
:fieldId="option.name"
:label="option.name"
:key="option"/>
</div>
</template>

Step 5 • Display options as checked

An option should appear as checked if the option’s id is included in the Array of ids (the value prop). Add the checked prop to the <check-box …>

<check-box
v-for="option in options"
:checked="value.includes(option.id)"
:fieldId="option.name"
:label="option.name"
:key="option"/>

To test if this worked, jump over to App.vue and update the heroes Array to

const heroes = ref([1,3,5]);

If it worked then Luther, Allison and Five should be checked 👍

Step 6 • Update the value when a checkbox is clicked

In the single checkbox we emit checked — but since these checkboxes are bound to an Array, we need to a little more. 💪

  1. Add/remove the clicked option id from the list of ids (the value)
  2. Then emit the updated list (to be caught by v-model on App.vue)

Add the setup method to the multi-checkbox component, and add the check method below.

setup(props, context) {
const check = (optionId, checked) => {
// copy the value Array to avoid mutating props
let updatedValue = [...props.value];
// remove name if checked, else add name
if (checked) {
updatedValue.push(optionId);
} else {
updatedValue.splice(updatedValue.indexOf(optionId), 1);
}
// emit the updated value
context.emit("update:value", updatedValue);
};
return {
check,
};
},

Then on each checkbox, listen for the @update event and call the check method. Be sure to pass the option.id and the $event (the $event is the checked boolean passed from the check-box )

<check-box
v-for="option in options"
:fieldId="option.name"
:checked="value.includes(option.id)"
:label="option.name"
@update:checked="check(option.id, $event)"
:key="option"
/>

Step 7 • Api interaction

So let’s say you’re working with existing data that you’d like to edit. If our hero selection app was real, we’d be interacting with an api that we would save our hero selection.

Api interaction is out of scope of this article, so we wont get too far into it.

In App.vue you’d fetch data during the onMounted() lifecycle hook, then update heroes to whatever the api returned.

Here’s the basic idea assuming you use axios:

<script>
import MultiCheckbox from "./components/multi-checkbox.vue";
import { ref, onMounted } from "vue";
export default {
name: "App",
setup() {
let heroes = ref([]);
const options = ref([
{ name: "Luther", id: 1 },
{ name: "Diego", id: 2 },
{ name: "Allison", id: 3 },
{ name: "Klaus", id: 4 },
{ name: "Five", id: 5 },
{ name: "Ben", id: 6 },
{ name: "Vanya", id: 7 },
]);
const getMyHeroes = async () => {
await axios.get('/heroes/').then(response =>
heroes.value = response.data.heroes
)
}
onMounted(() => {
getMyHeroes()
})
return {
heroes,
options,
};
},
components: {
"multi-check-box": MultiCheckbox,
},
};
</script>

In this situation you would also want to be hitting an endpoint to receive the options as well. And of course, you’d have a method that would post/put/patch the selected heroes to some endpoint. But i’ll let you handle all that 😉

This should cover the main changes — everything is in that codesandbox

Thanks for the great question Jessie Green

--

--