Search code examples
javascriptvue.jsinterpolationv-for

What is the proper way to output the result of a math function on a data property within a v-for loop in VueJS?


I've been working on a dice roller in Vue as part of a game. I loop through the types of dice with v-for to create a set of buttons and an affiliated div that will display the result. The issue is that even though my console logs are correct, I can't seem to get my rollResult to update where it should be interpolated. I've included only the necessary code to save all of your eyes. If I can provide anymore, please let me know. Thank you in advance!

HTML:

<v-list-tile v-for="die in dice" :key="die.name">
...
 <template v-slot:activator="{ on }">
      <v-btn class="primary" @click="rollDice(die.sides)">Roll</v-btn> 
          <div>{{rollResult}}</div>
</template>
...
</v-list-tile>

Data:

      rollResult: 0,
      dice: [
        { sides: 4 },
        { sides: 6 },
        { sides: 8 },
        { sides: 10 },
        { sides: 12 },
        { sides: 20 }
      ],

Function:

    rollDice: function(n) {
     let rollResult = Math.ceil(Math.random() * n);
        console.log(rollResult);
    }

Solution

  • You are creating a local variable, not mutating the state (the data). Use:

    rollDice: function(n) {
        this.rollResult = Math.ceil(Math.random() * n);
        console.log(this.rollResult);
    }
    

    Demo:

    new Vue({
      el: '#app',
      data: {
        message: 'Hello Vue.js!',
        rollResult: 0,
        dice: [
          { sides: 4 },
          { sides: 6 },
          { sides: 8 },
          { sides: 10 },
          { sides: 12 },
          { sides: 20 }
        ],
      },
      methods: {
        rollDice: function(n) {
            this.rollResult = Math.ceil(Math.random() * n);
            console.log(this.rollResult);
        }
      }
    })
    <script src="https://unpkg.com/vue"></script>
    
    <div id="app">
      <div v-for="die in dice" :key="die.name">
        <button class="primary" @click="rollDice(die.sides)">Roll {{ die.sides }}</button>
        <div>{{rollResult}}</div>
      </div>
    </div>

    If you need invididual results, turn rollResult into an array (or an object) and watch out for some caveats (like using Vue.set()):

    new Vue({
      el: '#app',
      data: {
        message: 'Hello Vue.js!',
        rollResult: [0, 0, 0, 0, 0, 0],
        dice: [
          { sides: 4 },
          { sides: 6 },
          { sides: 8 },
          { sides: 10 },
          { sides: 12 },
          { sides: 20 }
        ],
      },
      methods: {
        rollDice: function(n, index) {
            Vue.set(this.rollResult, index, Math.ceil(Math.random() * n));
            console.log(this.rollResult);
        }
      }
    })
    <script src="https://unpkg.com/vue"></script>
    
    <div id="app">
      <div v-for="(die, index) in dice" :key="die.name">
        <button class="primary" @click="rollDice(die.sides, index)">Roll {{ die.sides }}</button>
        <div>{{rollResult[index]}}</div>
      </div>
    </div>