Rodrigo.A92
Rodrigo.A92

Reputation: 751

Why Vuetify Checkbox works as expected only if I do a reading?

I'm having a weird behavior from the Vuetify componentv-checkbox.

The codepen shows 3 checkboxes per array element. I want to read the actual state of the checkbox (true or false) near each checkbox. Change the checkboxes's state and see it is being displayed correctly. Then, remove the line {{ counter }} and check again.

If you check a checkbox, it has to show 'true', and viceversa, if you uncheck it, it has to show 'false'. 'false' is the default value.

Actual Behavior

This is my code: - HTML:

 <div id="app">
  <div v-for="(row, index) in rows" :key="index">
    <v-layout row wrap>
      {{ row.item }}: 
      <v-card flat v-for="(friend, idx) in row.friends" :key="`msg-${idx}`">
        <v-checkbox
           v-model="friend.selected"
           :label="friend.name"
           color="red"
           hide-details
           @click.native="counter++"
        ></v-checkbox>
        {{ counter }}
        {{ friend.selected }}
      </v-card>
    </v-layout>
    <v-divider :key="`divider-${index}`"></v-divider>
  </div>
</div>
new Vue({
  el: "#app",
  data () {
    return {
      friendsAdded: ['Friend 1', 'Friend 2', 'Friend 3'],
      items: ['Place 1'],
      counter: 0,
    }
  },
  methods: {
    updateStatus(friend) {
     // Do something later
    },
  },
  computed: {
    rows() {
      const rows = [];
      for(let i = 0; i < this.items.length; i += 1) {
        const row = {};
        row.item = this.items[i];
        row.friends = [];
        for(let j = 0; j < this.friendsAdded.length; j += 1) {
          const friend = {};
          friend.name = this.friendsAdded[j];
          friend.selected = false;
          row.friends.push(friend);
        };
        rows.push(row);
      }
      console.log('rows: ', rows);
      return rows;
    }
  },
})

Here is codepen: https://codepen.io/rodrigoabb/pen/wYgzWW?editors=1010

Am I doing something obscenely wrong? How can I achieve the expected behavior without have to read that value (or something else)?

Thanks!

Upvotes: 1

Views: 2121

Answers (1)

Nati Mask
Nati Mask

Reputation: 676

One thing is wrong with your approach:

You are trying (by using v-model binded to friend.selected, which gets and also sets value) to set value of a computed property (the rows).

This is basically wrong: https://v2.vuejs.org/v2/guide/computed.html#Computed-Setter

What happens is:

The template was not sensitive to the change, although it actually occurs on the friends object. (Vuejs won't warn you about it because it is on a deep level of the computed object)

So, a re-render of your component never happened. But if that {{counter}} exists, that {{counter}} will be the sole reason for the re-render, which renders the whole template, including the {{friend.selected}}

With your simple case you can use the data function to create the rows array. Other options will be using separate data property to bind with the v-model, which will then affect the main rows array with watcher (if needed) or to use computed setter.

Example: Use data:

new Vue({
  el: "#app",
  data () {
    let items = ['Place 1'];
    let friendsAdded = ['Friend 1', 'Friend 2', 'Friend 3'];
    const rows = [];
    for(let i = 0; i < items.length; i += 1) {
      const row = {};
      row.item = items[i];
      row.friends = [];
      for(let j = 0; j < friendsAdded.length; j += 1) {
        const friend = {};
        friend.name = friendsAdded[j];
        friend.selected = false;
        row.friends.push(friend);
      };
      rows.push(row);
    }
    console.log('rows: ', rows);
    return {
      friendsAdded,
      items,
      counter: 0,
      rows
    }
  },
  methods: {
    updateStatus(friend) {
     // Do something later
    },
  },
})

Upvotes: 4

Related Questions