# Binding native events to your component in Vue.js
The main reason why i never tried to make certain reusable components like Input
, Button
, was because of binding native events on components. Creating props could be tiring until i discovered the $listeners
property. For example, creating a button
and input
component with props.
# With Props
# Button(src/components/Button.vue)
<template>
<button @click="onClick" class="tw-py-2 tw-px-3 tw-rounded-full">
<slot></slot>
</button>
</template>
<script>
export default {
name: "CustomButton",
props: {
onClick: {
type: Function,
required: true
},
},
};
</script>
<style lang="scss" scoped></style>
# Input(src/components/Input.vue)
<template>
<input
:type="type"
:placeholder="placeholder"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)",
@change = "onChange"
@focus = "onFocus"
@keyup = "onKeyUp"
@keydown = "onKeyDown"
/>
</template>
<script>
export default {
name: "CustomInput",
props: {
type: {
type: String,
required: true
},
placeholder: {
type: String,
required: true
},
value: {
type: String,
required: true
},
onChange: {
type: Function,
required: true
},
onFocus: {
type: Function,
required: true
},
onKeyUp: {
type: Function,
required: true
},
onKeyDown: {
type: Function,
required: true
}
},
};
</script>
Using this kind of component will be something like this
<custom-input
type="text"
placeholder = ""
v-model="firstName"
:onChange="func()"
:onFocus="focus()"
:onKeyUp="keyUp()"
:onKeyDown="keyDown()" />
<custom-button :onClick="nothing">
Done Here
</custom-button>
What if we wanted to use modifiers(.self
, .prevent
) with this props approach or probably more events like double-clicking on the Button? That would probably be hell However, the $listeners
make this easier, faster, readable, and less stressful. The $listeners
give the component some native experience.
# Using $listeners
# Refactoring Button Component
<template>
<button v-on="$listeners" class="tw-py-2 tw-px-3 tw-rounded-full">
<slot></slot>
</button>
</template>
<script>
export default {
name: "CustomButton"
};
</script>
<style lang="scss" scoped></style>
The v-on="$listeners"
does the magic; it binds all native event in specific DOM element to the button component. This makes it easier because we can now use all events we want. Something like this.
<custom-button @click="nothing" @dblclick="doesNothing" @mouseover="nothing">
Done Here
</custom-button>
# Refactoring Input Component
Using the $listeners
here as we did on the button will lead to an error because we already have the v-on
already listening for the input
event, so we try something else with $listeners
according to the Vue.js docs.
<template>
<input
:type="type"
:placeholder="placeholder"
v-bind:value="value"
v-on="$inputListeners"
/>
</template>
<script>
export default {
name: "CustomInput",
props: {
type: {
type: String,
required: true
},
placeholder: {
type: String,
required: true
},
value: {
type: String,
required: true
},
},
computed: {
inputListeners: function() {
const vm = this;
// `Object.assign` merges objects together to form a new object
return Object.assign(
this.$listeners,
// This ensures that the component works with v-model
input: function(event) {
vm.$emit("input", event.target.value);
}
}
);
}
},
};
</script>
Using this component now
<custom-input
type="text"
placeholder = ""
v-model="firstName"
@change.prevent="func()"
@focus="focus()"
@keyup.page-up="keyUp()"
@keydown.enter="keyDown()" />
This is better, simpler, and makes component design easy in Vue.js. Hopefully, in the future, I will write about how the $listeners
work in Vue.js.