Making a custom component shouldn't be a mess. And what's better than using Vue's native features to create one! So let's do it. Let's make a custom or manual or whatever-you-want-to-call-it component from scratch!
What are we makingβ
This:
Kickstart the custom toggle π¦΅
Step 1οΈβ£: The setup
Fire open your terminal to create a new Vue project:
vue create custom-toggle
If you don't have the Vue CLI installed in your machine, you can find the installation steps on their official website.
Remove all other files and create the new ones just like what you see in the following project structure:
custom-toggle
β
ββββpublic
β β index.html
ββββsrc
β β
β ββββcomponents
β β Toggle.vue
β β App.vue
β β main.js
ββββpackage.json
As you see, our custom component will live in ./components/Toggle.vue file.
Step 2οΈβ£: Code App.vue
This is our entry point component. As you see, we have two things to display. First, is the toggle component itself and the text below that tells us the state of the toggle, whether it's turned on or off.
Under the <script>
, use the data()
method to return
the default state of the component. Make it a boolean, so it can either be true
or false
. As I want to display the toggle to be "ON", hence I made it true
by default.
Next, register the component which we'll make in the next step using the components
option. Here's it's named appToggle
. Make sure you import it correctly. Then register it on to the template making sure to use Vue's v-model
directive to make two-way data-binding possible. Pass in the dataToggle
property so that it can check the state dynamically.
As for the text below, we simply have a <p>
tag which uses template
syntax's string interpolation.
App.vue:
<template>
<div>
<app-toggle v-model="dataToggle"></app-toggle>
<p style="text-align: center">Toggle: {{ dataToggle }}</p>
</div>
</template>
<script>
import Toggle from "./components/Toggle";
export default {
data() {
return {
dataToggle: true
};
},
components: {
appToggle: Toggle
}
};
</script>
Step 3οΈβ£: Code the toggle
Open up Toggle.vue file. Here we just need the two div
elements (as we're making a custom element) for the "ON" and "OFF" states. These will be wrapped in a parent toggleContainer
div
.
In order to style them as we want, just give each an id of on
and off
respectively. The entire CSS styles applied are basic, so here's the styling you need:
.toggleContainer {
margin-top: 40%;
display: flex;
justify-content: center;
align-items: center;
}
#on,
#off {
width: 40px;
height: 20px;
border-radius: 20px;
background-color: lightgray;
padding: 2px;
text-align: center;
margin: 10px -5px;
cursor: pointer;
}
#on:hover,
#on.active {
background-color: lightgreen;
}
#off:hover,
#off.active {
background-color: lightcoral;
}
Now comes the best part. In the script
we first need to define the props
object in order to pass data from App.vue to Toggle.vue. We only need the value
of the parent component. This is because the prop we pass will determine which toggle is clicked, the "ON" or the "OFF". It's also useful to add dynamic CSS property changes.
As you can see from the above CSS code we have different background-color
and active
/hover
pseudo-classes for both #on
and #off
. These are activated from the value
prop. But for this thing to work, we need to use Vue's class/style binding feature with v-bind
.
To implement this, we can either use v-bind:class
or the shorthand syntax i.e. just :class
. Here, just make the active
object to be value
(similar to dataToggle: true
) in case of "ON" and the opposite active: !value
(similar to dataToggle: false
) in "OFF".
But we didn't implement what to do when we actually click on each of these toggle options! Ready for the last step? Here we go!
Add the @click
click listener to the two children of the container. Pass on a new method switched()
to it as shown:
<template>
<div class="toggleContainer">
<div id="on" @click="switched(true)"
:class="{active: value}">ON</div>
<div id="off" @click="switched(false)"
:class="{active: !value}">OFF</div>
</div>
</template>
For the body of this method, we need to emit the new custom event on mouse click. Vue provides us with the $emit
syntax for this functionality. The first parameter is the type of custom event, in our case, it's an input
and the second parameter is isOn
.
Toggle.vue:
<template>
<div class="toggleContainer">
<div id="on" @click="switched(true)"
:class="{active: value}">ON</div>
<div id="off" @click="switched(false)"
:class="{active: !value}">OFF</div>
</div>
</template>
<script>
export default {
props: ["value"],
methods: {
switched(isOn) {
this.$emit("input", isOn);
}
}
};
</script>
<style scoped>
.toggleContainer {
margin-top: 40%;
display: flex;
justify-content: center;
align-items: center;
}
#on,
#off {
width: 40px;
height: 20px;
border-radius: 20px;
background-color: lightgray;
padding: 2px;
text-align: center;
margin: 10px -5px;
cursor: pointer;
}
#on:hover,
#on.active {
background-color: lightgreen;
}
#off:hover,
#off.active {
background-color: lightcoral;
}
</style>
And you've done it! π₯³ See how the text below the toggle changes from "true" to "false" and back!
You just made a custom toggle with Vue.js in a matter of minutes, that's what I like about this framework. It's really quick to make such components.
If you're stuck somewhere in the code or just want to interact with the output, I've embedded the CodeSandbox project below:
Where to next? π€
Brush up your custom component making skills with the following resources:
- Components Basics β Vue.js
- Creating Custom Inputs With Vue.js by Joseph Zimmerman
- Adding v-model Support to Custom Vue.js Components by Joshua Bemenderfer
- Learn Vue 2: Part 25 - Custom Input Components by Laracasts
Thanks for reading, I appreciate it! Have a good day. (βΏββΏββΏ)
Can you spot the difference? π
β Microsoft Developer UK (@msdevUK) September 10, 2020
Image source: https://t.co/rhSnxZLNJa#DevHumour #WFH #RemoteWorking pic.twitter.com/MRmH1aKB9u
Top comments (12)
This switch is not accessible, and not usable with keyboard navigation. You should really consider reading up on how to make custom components like these accessible before sharing wrong information.
Here are some good resources:
w3.org/TR/wai-aria-1.1/#switch
scottaohara.github.io/aria-switch-...
erikkroes.nl/blog/accessible-html-...
And I would add to this: styling based on an ID is not considered good practice, nor is using an ID like "on" or "off" in a component that will be reused. According to spec, ID should be unique on the whole page.
Hey Jordan, I agree, but this is not a full-fledged application. It's just a demo of how to start writing a custom component.
Also, please provide some external resources to back off your statements, this would help others to understand more.
Sure, I recognize that it's just a demo and there may be some merit in just getting things out there for people to see. That said, it's a demo targeted toward a fairly beginner level audience. These subtle behaviors are often not addressed in anything new programmers will learn, and I feel that it's a disservice to allow poor programming habits to show up in an example meant for teaching people. Arguably, you should never be okay with poor practices in any code you write, if they could be easily avoidable; but this problem is even worse when you have an audience that may have never seen any of the techniques and will learn bad habits while trying to understand everything going on in the example.
Short version: it being a demo is precisely the reason you should follow best practices.
Edit: first sentence in this doc developer.mozilla.org/en-US/docs/W...
Exactly β this may not be a full-fledged application, but if a new dev comes here, reads this, recreates this toggle with non-unique IDs, and reuse a bunch of that component on their page/app, they will end up with multiple non-unique IDs on their page. Its a bad practice, and a poor example for new developers.
Hi Pascal! You're right accessibility is highly important! But as you can see, this article is just focussed on "how to make a custom component". It's just about coding the component, adding other things like accessibility can surely be done. This was just to get a demo or a quick-start. :)
Also, thanks for adding the resources, they will definitely help!
Making it not accessible is not how you make a component. Youve just excluded a lot of people from being able to use the component, and even worse, youve taught a bunch of other people the wrong way to make a component.
If its not accessible, its not finished. βAddingβ accessibility is not a bonus; its a requirement
As another comment said that it is not a11y friendly. I strongly recommend to use
input[type=checkbox]
and use css to style it. Or, you can put aninput[type=checkbox]
that only visible to screen readers, then hide yourdiv
s on screen readers.Thanks for the info!
How did you create that GIF at the topmost?
Using Canva :)
Thanks Ayushi! Your comment is highly appreciated. π