DEV Community

Marina Mosti
Marina Mosti

Posted on

Demystifying the v-model Directive in Vue

This post was originally published at 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.

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

<input type="email" id="email" />
$('#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.

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.

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

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

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.

<input 
  type="email" 
  :value="email" 
  @input="doSomethingWith"  
/>
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.

<input 
  type="email" 
  :value="email" 
  @input="doSomethingWith"  
/>
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.

<input type="email" v-model="email" />
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, or the last part of my Vue for Beginners Series 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

PS. All hail the magical avocado 🥑

PSS. ❤️🔥🐶☠️

Top comments (13)

Collapse
 
256hz profile image
Abe Dolinger

Cool. Are there any downsides? Coming from React where one-way binding is a religious tenet, this seems super convenient.

Collapse
 
marinamosti profile image
Marina Mosti

Hey Abe, not sure what you mean downsides? v-model is a super powerful tool, and when you learn how to deconstruct it into bindings and events, you get even more out of it and granular control :)

Collapse
 
thepeoplesbourgeois profile image
Josh

Your writing is stellar

Collapse
 
marinamosti profile image
Marina Mosti

Thanks Josh! :D

Collapse
 
thepeoplesbourgeois profile image
Josh • Edited

Thank you, for the informative and actually-lol'd write-up!

Collapse
 
artistro08 profile image
Devin Green

As a beginner developer, this article speaks a lot to me. Thank you 🙏🏾

Collapse
 
marinamosti profile image
Marina Mosti

I'm really glad this was of use for you :D

Collapse
 
global_codess profile image
Faith Mueni Kilonzi

I really enjoyed this! I am in love with your writing.

Collapse
 
marinamosti profile image
Marina Mosti

Thank you! :)

Collapse
 
johandalabacka profile image
Johan Dahl

Nice and very clear article

Collapse
 
marinamosti profile image
Marina Mosti

Thanks for reading Johan!

Collapse
 
joluga profile image
joluga

I was eating avocado while reading this, Seriously. :)

Collapse
 
marinamosti profile image
Marina Mosti

Sharing is caring 🥑