# 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.

More from SoftNexus