Muhi Logo Text
Work With MeAboutTestimonialsBlog

How to Create Custom v-model for Web Components

Last updated on May 29, 2021

vue
web components
Vue Web Component

I wrote in a previous article the importance of Web Components and how to make them work with React. Today, I’m focusing on a Vue example with a Web Component input element.

Fortunately, Vue fully supports Web Components according to custom-elements-everywhere.com but I faced a small inconvenience while using v-model directive with an input element.

V-model is a built-in directive in Vue that makes data binding easier with less code.

The code below:

<input v-model="searchText">

Is the same as:

<input
  :value="searchText"
  @input="searchText = $event.target.value"
>

Currently, we can’t use v-model in a Web Component but we can create our own custom directive to make it behave the same way across all custom elements!

For simplicity purposes, I’m using Wired Elements for the Web Components example. Wired Elements are a set of common UI elements with a hand-drawn, sketchy look. The library uses LitElement for creating Web Components.

Create Vue App

Install Vue CLI and create a new project.

npm install -g @vue/cli @vue/cli-service-global
vue create vue-custom-v-model-example

Install Wired Elements

Full documentation on Wired Elements is available on their main repository

npm i wired-elements

Import Input Element:

In this section, we will add a Wired Input Element and show how the v-model won’t work the way it usually does with the native input element.

<template>
  <article>
    <wired-input v-model="txtValue"></wired-input>
    {{txtValue}}
  </article>
</template>

<script>
import 'wired-input';

export default {
  name: 'App',
  components: {
  },
  data() {
    return {
      txtValue: ''
    }
  }
}
</script>

<style>
#app {
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Quick summary of the code above:

  • Imported wired-input
  • Created a new txtValue data to use in v-model
  • Print out the txtValue on the screen as we are typing

Now as expected, when typing, nothing is printed:

Create a custom directive

Let’s create a new directive and call it custom-model.js. The directive will listen to the input event and update the binding value.

import Vue from "vue";

Vue.directive("custom-model", {
  bind(el, binding, vnode) {
    const inputHandler = (event) =>
      (vnode.context.$data[binding.expression] = event.target.value);
    el.addEventListener("input", inputHandler);
  },
});

Here are some details of what we did in the code above:

  • In line 4, we are using the bind hook function (when the directive is first bound to the element)
  • In line 6, we are accessing the context data from vnode object to update the binding value in the inputHandler function.
  • Line 7, simply add an event listener to the element

Test input element with the custom directive

Now that we have the directive ready, let’s apply it to the web component.

First, let’s update App.vue to use the new directive:

<template>
  <div id="app">
    <wired-input v-custom-model="txtValue" />
    {{txtValue}}
  </div>
</template>

<script>
import 'wired-input';
import CustomModelDirective from './directives/custom-model';

export default {
  name: 'App',
  components: {
  },
  data() {
    return {
      txtValue: ''
    }
  },
  directives: {
    CustomModelDirective
  }
}
</script>

<style>
#app {
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Here is a demonstration of the final working code:

You can access the complete repository here.

Bye for now 👋

If you enjoyed this post, I regularly share similar content on Twitter. Follow me @muhimasri to stay up to date, or feel free to message me on Twitter if you have any questions or comments. I'm always open to discussing the topics I write about!

Recommended Reading

Learn how to validate table rows and input fields with BootstrapVue and HTML form validation.

vue
bootstrap vue

Discussion

Upskill Your Frontend Development Techniques 🌟

Subscribe to stay up-to-date and receive quality front-end development tutorials straight to your inbox!

No spam, sales, or ads. Unsubscribe anytime you wish.

© 2024, Muhi Masri