Vue_870x220

Learn about the v-model directive in Vue.js, and get to know the basics of two-way binding and how you can use it in your own app development.

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

Oftentimes we find ourselves describing the v-model directive as a magical entity that allows creating a two-way binding 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, using type email.

<input type="email" />

The problem is simple: We need to be able to know what the user types in here. And we may need to 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 disappointed. ☹️)

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, makes 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 during 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 rewrite the whole 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 it’s 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 on v-model.


Marina Mosti
About the Author

Marina Mosti

Marina Mosti is a full-stack web developer with over 13 years of experience in the field. She enjoys mentoring other women on JavaScript and her favorite framework, Vue, as well as writing articles and tutorials for the community.

She currently holds a position as Lead FE Developer at VoiceThread, and she is the author of the FormVueLatte library as well as a member of the Vuelidate team. In her spare time, she enjoys playing bass, drums, and videogames.

Related Posts

Comments

Comments are disabled in preview mode.