bekwam courses

Vue.js Array v-model Use

June 18, 2020

This article demonstrates using v-model to record the state of an array of checkbox selections. A snippet of the Vue source code will be presented to verify that Vue.set is called behind-the-scenes to fend off a Vue Caveat.

In the CodePen presented below, a stack of three checkboxes will turn a tag at the base of the screen green (true) when all are selected. Checkthe boxes in the CodePen and verify that selecting all will turn the tag at the base green (true) and that any other combination will turn the tag red (false). The code is reactive and updates the state of the tag immediately.

See the Pen Vue Array v-model Demo by Carl Walker (@walkerca) on CodePen.

The following annotated screenshot shows the UI elements and their relation to structures found in the Vue component. The checkboxes are bound by v-model to an array of items located in the data section. The bottom paragraph and tag are bound (one-way) to a computed.

Annotated Screenshot of CodePen
Checkboxes Bound to Array; Tag Bound to Computed

This is the data section and the computed section of the component presented in the CodePen. items is a simple collection of booleans, but this example could be expanded to more complex objects. The computed uses the standard JavaScript function every to test that all items are set. Since the item is boolean, the arrow function is a simple identity.


data: {
    items: [
      false,
      false,
      false
    ]
},
computed: {
    allSelected() {
      return this.items.every( itm => itm );
    }
}    

In the template part, there is a v-for which iterates over the array, creating a checkbox for each item. The input from the user will be saved in the items array through the v-model binding on the input checkbox. Notice the array expression.


<div class="content" v-for="(itm,index) in items" :key="index">
	<div class="control">
	  <div class="field">
	    <label class="checkbox">{{ index }}
	    <input type="checkbox" v-model="items[index]" />
	    </label>
	  </div>
	</div>
</div>  

You may recall from the Vue Caveats that you can't make an array assignment like items[index] = !items[index] reactive. You need to use Vue.set (or $set). v-model does this. Taking a look at the Vue source code in vue-template-compiler/browser.js confirms this.


  function genAssignmentCode (
    value,
    assignment
  ) {
    var res = parseModel(value);
    if (res.key === null) {  // is there a [] in the expression?  if so, we have a key
      return (value + "=" + assignment)
    } else {
      return ("$set(" + (res.exp) + ", " + (res.key) + ", " + assignment + ")") // Vue.set called on an array
    }
  }

This is also the reason why v-model="itm" wouldn't work. Although you're dealing with the same object (items[index] versus itm), the scalar reference to item wouldn't trigger the special Vue.set handling.

The following section of the template shows the handling of the allSelected() computed. The boolean is printed in two places and used in a dynamic CSS class expression to change the color from red to green.


<p class="content">
    All selected? {{ allSelected }}
    <div class="tags has-addons">
      <span class="tag">All selected?</span>
      <span class="tag" :class="{ 'is-success' : allSelected, 'is-danger': !allSelected }">{{ allSelected }}</span>
    </div>
</p>

From my time on the Vue Land Discord, I've noticed that Vue Caveats are a big source of frustration for beginners. To the point of being a Bot, I paste in a link to the docs in response to a "reactivity isn't working" question. This article shows how that same Vue.set is used with v-model although the details are well-hidden from developers.


Headshot of Carl Walker

By Carl Walker

President and Principal Consultant of Bekwam, Inc