Demystifying the v-model Directive in Vue
Tags: vue, vmodel, javascript, beginners
Published: 2019-10-26 16:16:33

This post was originally published at [https://www.telerik.com/blogs/demystifying-the-v-model-directive-in-vue](https://www.telerik.com/blogs/demystifying-the-v-model-directive-in-vue)

---

More often than not I get comments and messages asking me to go into detail about `v-model` by people that have read an article or attended a workshop and the _magic_ of `v-model` is touched on but not thoroughly explained.

Today, we will go into detail on what exactly this directive does for us in Vue, and a top-level glance at how it works behind the scenes.

This article is intended for novice and intermediate users who want to further their understanding of the directive, and I am assuming a general basic knowledge of Vue as a whole.

## Two-way binding and the basics

Often times we find ourselves describing the `v-model` directive as a magical entity that allows creating a two way biding to an input element. But what exactly does the two way binding mean? And why should you care?

Vue and other frameworks like it have a bunch of _magical_ methods and ways of doing things, `v-model` is a great example of this type of thing. 

The entry-level knowledge required to use it is minimal because frankly, you don't really NEED to understand how it works in order to use it - but when you fully grasp the concept behind it the _way_ you use it or think about it changes.

## Listen to user input

Let's start with a simple input element. It will be of type email.

```html
<input type="email" />
```

The problem is simple, we need to be able to _know_ what the user types in here. We need to maybe send it to the back end for them to log the user in, or to capture it for a registration form.

How would you approach this using jQuery or vanilla JS?

In jQuery maybe you would add an `id` attribute to the element, and target it directly to extract the value.

```html
<input type="email" id="email" />
```

```js
$('#email').val();
```

The problem with this approach is that you are stuck having to then add an event listener if you want to react to keystrokes because so far you are getting a static value at the moment the code is executed. It is NOT _reactive_. 

Let's try this again with an event listener and vanilla JS.

```js
const el = document.querySelector('#email');
el.addEventListener('input', function(event) {
    // when the user types this will fire
    const inputValue = event.target.value;
    doSomethingWith(inputValue);
});
```

Alright, we're getting somewhere! So far we are able to call the function `doSomethingWith` with the event's value (what the user typed). This seems like a lot of code though, and what happens if we have a form with 30 different inputs?

Let's do it the Vue way. We are going to add an event listener to the input and call our fake `doSomethingWith` function every time it fires.

```html
<input type="email" @input="doSomethingWith" />
```

I don't know about you, but this seems like magical avocado mischief to me. How does Vue accomplish the same thing behind the scenes?

First of all, notice that we don't need an `id` anymore. In fact, I would argue that using `id` in Vue is a terrible idea! 

If you use ids in Vue, and you use the component in several places then you are going to have several instances of an element with the same id - which spells out CHAOS. 

Your developer avocado has gone bad, frand. GG. 🥑☠️

Let's go back to our example though when we add `@input` to our element, Vue is smart enough to attach the necessary event listener to this particular element via reference. It will also handle _removing_ this event listener for us!

Finally, it will call the function that we passed inside the `" "` whenever the event is fired, and it will pass it the `event` object. Neat!

## Changing the input programmatically

Let's move on to problem #2.

You managed to listen to the events of the user making inputs on your field, good work! (Hopefully using Vue and not jQuery, come on. I am disappoint. ☹️)

Now, part two of "two-way binding". What if we want to dynamically do something with the user's email and have the input reflect the change? 

Maybe we have some sort of form autocomplete, or validation, or we have another input element that will prepopulate their name from the database. There's a lot of possible scenarios.

Let's approach this problem with jQuery first. 🤢

```js
// This is the value we are storing somewhere
// So that later we can send it to the backend
const userEmail = 'somevalue@theuserentered.com';

$('#email').on('input', function() {
    userEmail = $('#email').val();
});

// Now what if we want to change the email on the input programmatically?
function changeEmail(newEmail) {
    $('#email').val(newEmail);
    userEmail = newEmail;
}

changeEmail('your-email@is-wrong.com');
```

You can see from this last example how quickly this can start to get really messy. Monolithic files of jQuery for event handling and input validation are a thing of the past!

You can also appreciate how it's going to be a problem keeping a `state`. We have a high-level variable `userEmail` that is keeping the value, and we have to be careful that we are orderly about our code. Now do this 40 times for a big form, please.

One thing that you may have also not considered at this point is that we are trying to be really careful about setting the `.val` of our input when we change it on the `changeEmail` function. But what if another dev, or even ourselves make another function that modifies the `userEmail` variable somewhere else?

We have to keep in mind that every time this variable changes the input has to be updated, or we have to get into some rather advanced javascript that will set up getters and setters for us to fix that reactivity problem.

Let's approach this second problem in Vue. We are going to create first a local state in our make-believe component.

```js
data() {
    return {
        email: ''
    }
}
```

Now that we have our local state, we have to tell the input to use it and bind it to the value.

```html
<input 
  type="email" 
  :value="email" 
  @input="doSomethingWith"  
/>
```

```js
methods: {
    doSomethingWith(event) {
        this.email = event.target.value;
        // Do other stuff, eat avocados, play zelda and admire a raccoon
    }
}
```

That's it! Every time the `email` state changes, the input will be updated accordingly. We now have two ways of binding to the input.

First, when our local state changes. Second, when the user types on the field, the `input` listener will update the `state` with the value. When the state updates, it will update the input. 

Do you see the cycle? DO YA?

## Enter v-model

The kind folks at the Vue realized that this pattern of adding two one-way bindings, one that feeds _into_ the input, and one that feeds _out_ of the input was very common when dealing with forms and user data.

Thus, the magical avocado and the `v-model` directive were born. Both were cared for and nurtured, and the magical avocado went bad throughout the night and we had to toss it out. But such is life.

What happens then when you have to two-way bind your inputs, do you have to go through this double process where you bind the `:input` to some sort of state, and then listen to an event and re-write all the state?

The answer is no! `v-model` your friendly neighborhood avocado to the rescue.

We currently have this for our form input.

```html
<input 
  type="email" 
  :value="email" 
  @input="doSomethingWith"  
/>
```

```js
data() {
    return {
        email: ''
    }
},
methods: {
    doSomethingWith(event) {
        this.email = event.target.value;
        // Do other stuff, eat avocados, play zelda and admire a raccoon
    }
}
```

And with the power invested in me by Vue, and the blessing of captain planet (yes, I'm old), we can make it all nice and simple.

```html
<input type="email" v-model="email" />
```

```js
data() {
    return {
        email: ''
    }
}
```

That's it! `v-model` will make sure that the correct event is being listened to (in the case of native elements like inputs, selects, etc.) and then bind our local `email` data property to it! Ah-two, ah-way, ah-binding. 👌

## Conclusion

Keep in mind, `v-model` has some caveats regarding which property it has to bind to and which event it has to listen to.

Vue is super smart regarding this behind the scenes when being used on inputs, selects, checkboxes and radio buttons - but when you are working with custom components you are going to have to do this heavy lifting yourself.

This, however, is out of the scope of this beginner article. But you can check out this reference on `v-model` on custom components on the [official documentation](https://vuejs.org/v2/guide/components.html#Using-v-model-on-Components), or the last part of my [Vue for Beginners Series](https://dev.to/vuevixens/hands-on-vuejs-for-beginners-part-7-3e1c) where I touch up on a little about `v-model`.

As always, thanks for reading and share with me your experiences with v-model on twitter at: [@marinamosti](http://www.twitter.com/marinamosti)

PS. All hail the magical avocado 🥑

PSS. ❤️🔥🐶☠️