Previously, We have created a ToggleButton component with Vue. We have used the standard way to build the component. In this article, we are going to create the class-based component with TypeScript. We have used the followings for toggle button component i.e.
The same we are going to achieve through the class-based component. It is not a recommended way but if you enjoyed writing codes through Classes and TypeScript then you can.
If you are starting through a fresh project with Vue CLI then you have to add the TypeScript to your project through custom setting during create project.
Or you can add the following dependency packages through npm.
npm i vue-class-component vue-property-decorator -D
Your Class-based Component
Now create a ToggleButton.vue
file under src/component directory and paste the following codes.
<template>
<label for="_button" class="toggle__button">
<span class="toggle__label">Off</span>
<input type="checkbox" id="_button">
<span class="toggle__switch"></span>
</label>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
@Component
export default class ToggleButton extends Vue {}
</script>
As you can see, we have used the vue-property-decoration
package. The @Component
is a decoration that indicates the class is a vue component. Note that our class-based component must extend the parent class i.e. Vue
.
Not doubt that It’s pretty simple with a standard way to create the component.
<script>
export default {}
</script>
Component Properties
Let’s define the properties of our class-based component. We are going to use the @Prop()
, decorator. Take a look at the codes.
<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator';
@Component
export default class ToggleButton extends Vue {
@Prop({default: false})
disabled: boolean
@Prop({default: false})
defaultState: boolean
}
</script>
In my case, we have to define the default value for the related properties. Here we defined the default value i.e. true
for disabled properties that will accept the boolean
type value.
@Prop({default: false})
disabled: boolean
Which is similar to this code.
props: {
disabled: {
type: Boolean,
default: false
}
}
And if you don’t want to define the default value then you can use the @Prop()
decorator similar to this code.
@Prop()
disabled: boolean
Property ‘disabled’ has no initializer and is not definitely assigned in the constructor
You may get an error while defining the component properties. That is because of the Strict Class Initialization in TypeScript latest version. You need to add !
with property to fix the property initializer error.
@Prop({default: false})
disabled!: boolean
@Prop({default: false})
defaultState!: boolean
Initial Data Declartion
Let’s move to the initial data declaration section. The Initial data can be declared as instance property such as:
currentState: boolean = true
Here the currentState
is the property of the class with the default true
value that only accepts the boolean value. Which is similar to the following codes.
data() {
return {
currentState: this.defaultState
}
}
The complete code for our component should look like this:
import { Component, Vue, Prop } from 'vue-property-decorator';
@Component
export default class ToggleButton extends Vue {
@Prop({default: false})
disabled: boolean
@Prop({default: false})
defaultState: boolean
/**
* Initial data
*/
currentState: boolean = this.defaultState
}
Computed Properties
As we know that computed properties are by default getter-only. We just have to add the get
before the method to create the computed property. This is how we can define the computed properties in the class-based component.
get isActive() {
return this.currentState
}
Which is similar to the codes like:
computed: {
isActive() {
return this.currentState;
}
}
But what if we need to set the computed property? Let’s understand it with another piece of code.
get checkedValue(): boolean {
return this.currentState
}
set checkedValue(newValue) {
this.currentState = newValue;
}
Here we have defined a computed property i.e. checkedValue()
with getter and setter. Which is similar to these code.
checkedValue: {
get() {
return this.currentState;
},
set(newValue) {
this.currentState = newValue;
}
}
Isn’t easy?
Watchers
The next step is defining the watchers. The @Watch()
decorator is used to defining the watchers.
@Watch('defaultState')
onPropertyChanged() {
this.currentState = this.defaultState
}
Here we have attached the @Watch()
decorator to the defaultState
property. Which is similar to the following codes.
watch: {
defaultState: function defaultState() {
this.currentState = Boolean(this.defaultState)
}
}
Complete Class-based Component Codes
<template>
<label for="_button" :class="{'active': isActive}" class="toggle__button">
<span v-if="isActive" class="toggle__label">{{ enableText }}</span>
<span v-if="! isActive" class="toggle__label">{{ disableText }}</span>
<input type="checkbox" id="_button" v-model="checkedValue">
<span class="toggle__switch"></span>
</label>
</template>
<script lang="ts">
import { Component, Vue, Prop, Emit, Watch } from 'vue-property-decorator';
@Component
export default class ToggleButton extends Vue {
@Prop({default: false})
disabled!: boolean
@Prop({default: false})
defaultState!: boolean
@Prop({default: 'On'})
labelEnableText!: string
@Prop({default: 'Off'})
labelDisableText!: string
@Emit()
change(): boolean {
return this.currentState;
}
/**
* Initial data
*/
currentState: boolean = this.defaultState
get isActive(): boolean {
return this.currentState
}
get enableText(): string {
return this.labelEnableText
}
get disableText(): string {
return this.labelDisableText
}
get checkedValue(): boolean {
return this.currentState
}
set checkedValue(newValue) {
this.currentState = newValue;
}
@Watch('defaultState')
onPropertyChanged() {
this.currentState = this.defaultState
}
}
</script>
<style lang="scss" scoped>
$base-darken: #666666;
$base-active-darken: #53B883;
.toggle__button {
vertical-align: middle;
user-select: none;
cursor: pointer;
input[type="checkbox"] {
opacity: 0;
position: absolute;
width: 1px;
height: 1px;
}
.toggle__switch {
display:inline-block;
height:12px;
border-radius:6px;
width:40px;
background: rgba($base-darken, 0.2);
box-shadow: inset 0 0 1px rgba($base-darken, 0.2);
position:relative;
margin-left: 10px;
transition: all .25s;
&::after,
&::before {
content: "";
position: absolute;
display: block;
height: 18px;
width: 18px;
border-radius: 50%;
left: 0;
top: -3px;
transform: translateX(0);
transition: all .25s cubic-bezier(.5, -.6, .5, 1.6);
}
&::after {
background: $base-darken;
box-shadow: 0 0 1px $base-darken;
}
&::before {
background: $base-darken;
box-shadow: 0 0 0 3px rgba($base-darken, 0.1);
opacity:0;
}
}
}
.active {
.toggle__switch {
background: rgba($base-active-darken, 0.2);
box-shadow: inset 0 0 1px rgba($base-active-darken, 0.2);
&::before,
&::after {
transform:translateX(0px);
}
&::after {
left: 23px;
background: $base-active-darken;
box-shadow: 0 0 1px $base-active-darken;
}
}
}
</style>
You can use this component in App.vue
file with <ToggleButton />
tag.
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import ToggleButton from './components/ToggleButton.vue';
@Component({
components: {
ToggleButton
},
})
export default class App extends Vue {}
</script>