Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vuex nested loop, how to handle v-model on select/option

Tags:

vue.js

vuex

In my application I need to use a nested v-for to display a list of elements with a select-option.. This is the scenario

<div class="stuck" v-for="box in items">
  <p>Pick an option for this box:</p>
  <select v-model="box">
      <option v-for="package in packages" 
              :value="package.id">{{ package.name }} </option>
  </select>
</div>

The variable items come from Vuex store. In this way, i'm getting the error:

You are binding v-model directly to a v-for iteration alias. This will not be able to modify the v-for source array because writing to the alias is like modifying a function local variable. Consider using an array of objects and use v-model on an object property instead.

With this in mind, i'm going to change the code like so:

<div class="stuck" v-for="box in items">
  <p>Pick an option for this box:</p>
  <select v-model="box.id">
      <option v-for="package in packages" 
              :value="package.id">{{ package.name }} </option>
  </select>
</div>

I've just changed the select v-model from the alias box, to the right id: box.id

In this way, all works... or... half works. Because, if i'm going to pick an option from the select, i got another error:

[vuex] Do not mutate vuex store state outside mutation handlers.

This is correct, because the v-model is bind to box.id (that is not an alias but a real value). But, when i pick an option the v-model "try" to change box.id that come from Vuex store.

Now, in a simple scenario i will create a computed property for set/get to avoid vuex store mutation.

But... here i have a nested loop, so i cant create a computed on 'box.id'.

Do you have a solution for this ?

Thanks a lot!

like image 947
Mistre83 Avatar asked Jan 28 '17 21:01

Mistre83


1 Answers

you could try a different data flow pattern. Your select listens to the store (but does not directly update it)

<div class="stuck" v-for="box in items">
  <p>Pick an option for this box:</p>
  <select :value="box.id" @change="updateBox">
    <option v-for="package in packages" :value="package.id">
      {{ package.name }}
    </option>
  </select>
</div>

Then you create a method that fires whenever the selected option changes

updateBox(e) {
  const id = e.target.value;
  this.$store.commit('changeYourBox', id);
},

This function should commit a vuex mutation that alteres the box id. So you'd need that mutation too. Once the store value updates, your components box object updates and the select that listens to that store will update it's selected value accordingly.

That way, you can alter the store value from anywhere and the selected value will change as well.

like image 175
ProblemsOfSumit Avatar answered Oct 13 '22 17:10

ProblemsOfSumit