Vue v-model Directive

Compared to normal JavaScript, it is easier to work with forms in Vue because the v-model directive connects with all types of input elements in the same way.

v-model creates a link between the input element value attribute and a data value in the Vue instance. When you change the input, the data updates and when the data changes, the input updates as well (two-way binding).

Two-way Binding

As we have already seen in the shopping list example on the previous page, v-model provides us with a two-way binding, meaning that the form input elements update the Vue data instance, and a change in the Vue instance data updates the inputs.

The example below also demonstrates the two-way binding with v-model.

Example

Two-way binding: Try to write inside the input field to see that the Vue data property value gets updated. Try also to write directly in the code to change the Vue data property value, run the code, and see how the input field is updated.

<div id="app">
  <input type="text" v-model="inpText">
  <p> {{ inpText }} </p>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        inpText: 'Initial text'
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

Note: The v-model two-way binding functionality could actually be achieved with a combination of v-bind:value and v-on:input:

  • v-bind:value to update the input element from the Vue instance data,
  • and v-on:input to update the Vue instance data from the input.

But v-model is much easier to use so that is what we will do.


A Dynamic Checkbox

We add a checkbox to our shopping list on the previous page to mark if an item is important or not.

Next to the checkbox we add a text that allways reflects the current 'important' status, changing dynamically between 'true' or 'false'.

We use v-model to add this dynamic checkbox and text to improve user interaction.

We need:

  • a boolean value in the Vue instance data property called 'important'
  • a checkbox where the user can check if the item is important
  • a dynamic feedback text so that the user can see if the item is important

Below is how the 'important' feature looks, isolated from the shopping list.

Example

The checkbox text is made dynamic so that the text reflects the current checkbox input value.

<div id="app">
  <form>
    <p>
      Important item?
      <label>
        <input type="checkbox" v-model="important">
        {{ important }}
      </label>
    </p>
  </form>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
       important: false
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

Let's include this dynamic feature in our shopping list example.

Example

<div id="app">
  <form v-on:submit.prevent="addItem">
    <p>Add item</p>
    <p>Item name: <input type="text" required v-model="itemName"></p>
    <p>How many: <input type="number" v-model="itemNumber"></p>
    <p>
      Important?
      <label>
        <input type="checkbox" v-model="itemImportant">
        {{ important }}
      </label>
    </p>
    <button type="submit">Add item</button>
  </form>
  <hr>
  <p>Shopping list:</p>
  <ul>
    <li v-for="item in shoppingList">{{item.name}}, {{item.number}}</li>
  </ul>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        itemName: null,
        itemNumber: null,
        important: false,
        shoppingList: [
          { name: 'Tomatoes', number: 5, important: false }
        ]
      }
    },
    methods: {
      addItem() {
        let item = {
          name: this.itemName,
          number: this.itemNumber
          important: this.itemImportant
        }
        this.shoppingList.push(item)
        this.itemName = null
        this.itemNumber = null
        this.itemImportant = false
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

Mark Found Items in The Shopping List

Let's add functionality so that items added to the shopping list can be marked as found.

We need:

  • the list items to react on click
  • to change the status of the clicked item to 'found', and use this to visually move the item away and strike it through with CSS

We create one list with all items we need to find, and one list below with items found striked through. We can actually put all the items in the first list, and all the items in the second list, and just use v-show with the Vue data property 'found' to define whether to show the item in the first or second list.

Example

After adding items to the shopping list we can pretend to go shopping, clicking the items away after finding them. If we click an item by mistake we can take it back to the 'not found' list by clicking the item once more.

<div id="app">
  <form v-on:submit.prevent="addItem">
    <p>Add item</p>
    <p>Item name: <input type="text" required v-model="itemName"></p>
    <p>How many: <input type="number" v-model="itemNumber"></p>
    <p>
      Important?
      <label>
        <input type="checkbox" v-model="itemImportant">
        {{ important }}
      </label>
    </p>
    <button type="submit">Add item</button>
  </form>

  <p><strong>Shopping list:</strong></p>
  <ul id="ulToFind">
    <li v-for="item in shoppingList"
        v-bind:class="{ impClass: item.important }"
        v-on:click="item.found=!item.found"
        v-show="!item.found">
          {{ item.name }}, {{ item.number}}
    </li>
  </ul>
  <ul id="ulFound">
    <li v-for="item in shoppingList"
        v-bind:class="{ impClass: item.important }"
        v-on:click="item.found=!item.found"
        v-show="item.found">
          {{ item.name }}, {{ item.number}}
    </li>
  </ul>

</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const app = Vue.createApp({
    data() {
      return {
        itemName: null,
        itemNumber: null,
        important: false,
        shoppingList: [
          { name: 'Tomatoes', number: 5, important: false, found: false }
        ]
      }
    },
    methods: {
      addItem() {
        let item = {
          name: this.itemName,
          number: this.itemNumber,
          important: this.itemImportant,
          found: false
        }
        this.shoppingList.push(item)
        this.itemName = null
        this.itemNumber = null
        this.itemImportant = false
      }
    }
  })
  app.mount('#app')
</script>
Try it Yourself »

Use v-model to make The Form Itself Dynamic

We can make a form where the customer orders from a menu. To make it easy for the customer, we only present the drinks to choose from after the customer chooses to order drinks. This is can be argued to be better than presenting the customer with all items from the menu at once. In this example we use v-model and v-show to make the form itself dynamic.

We need:

  • A form, with relevant input tags and 'Order' button.
  • Radio-buttons to select 'Dinner', 'Drink' or 'Dessert'.
  • After category is chosen, a dropdown menu appears with all the items in that category.
  • When an item is chosen you see an image of it, you can choose how many and add it to the order. The form is reset when the item is added to the order.

Example

This form is dynamic. It changes based on user choices. The user must first choose category, then product and how many, before the order button becomes visible and the user can order it.

Try it Yourself »

Vue Exercises

Test Yourself With Exercises

Exercise:

Provide the correct code so that the default browser refresh on submit is prevented.

Also, provide code so that the input field values get a two-way binding to the Vue data instance properties 'itemName' and 'itemNumber'.

<form v-on:="addItem">
  <p>Add item</p>
  <p>
    Item name: 
    <input type="text" required ="itemName">
  </p>
  <p>
    How many: 
    <input type="number" ="itemNumber">
  </p>
  <button type="submit">Add item</button>
</form>

Start the Exercise



Copyright 1999-2023 by Refsnes Data. All Rights Reserved.