1

I have an applicated that requires the update of two fields that depend on each other for their values.

For example:

<template>
    <tr>
        <td>{{total}}</td>
        <td><input type="text" v-model="calculateEarnedPercentage" @change="updatedForecastPercentage"></td>
        <td><input type="text" v-model="spent_dollar"></td>
    </tr>
</template>

<script>
    export default {
        data () {
            return {
                total: 1000000,
                spent_percentage: '',
                spent_dollar: 20000,
            }
        },
        methods: {
            updatedForecastPercentage () {
                this.vendor.regional_forecast_dollar = this.vendor.purchases / (this.vendor.regional_forecast_dollar / 100)
            }
        },
        computed: {
            calculateEarnedPercentage () {
                return (this.vendor.regional_forecast_dollar / this.vendor.purchases) * 100
            }
        }
    }
</script>

The two "spent" values depend on a static "total" value. I will be storing the spent_dollar, and the percentage will be initially derived from that.

Now if the user updates percentage, I need the dollar value to update. If they update the dollar value, I need the percentage to update.

As of now it obviously doesn't work. Circular updates are happening. How do you design your data to allow this functionality in Vue.js?

Gurnzbot
  • 3,742
  • 7
  • 36
  • 55
  • One is a real data item, the other is a writable computed. – Roy J May 08 '17 at 19:48
  • 1
    If you are doing `v-model="computed property"`, then consider defining a `setter` for this computed property (`set` prop). Inside this setter you can apply some logic to your variables depending on the changed resulting value. Read about it: https://vuejs.org/v2/guide/computed.html#Computed-Setter – Egor Stambakio May 08 '17 at 19:59
  • @wostex thank you! I was not aware of the get/set dynamic. – Gurnzbot May 08 '17 at 20:12

1 Answers1

2

Looks like you could use some watches and a mutex. Taking an idea from parallel processing I built a JSfiddle to showcase this idea

<div id="app">
  <span>{{ total }}</span>
  <span><input type="text" v-model.number.lazy="spent_percentage"></span>
  <span><input type="text" v-model.number.lazy="spent_dollar"></span>
  
  <pre>{{ $data }}</pre>
</div>
 new Vue({
      el: '#app',
      data () {
        return {
          total: 1000000,
          spent_percentage: 5.0,
          spent_dollar: 20000,
          mutex: false,
          vendor: {
            purchases: 2358,
            regional_forecast_dollar: 1
          }
        }
      },
      watch: {
        spent_percentage: function(value, old_value) {
          if (!this.mutex) {
            this.mutex = true
            
            this.spent_dollar = (this.vendor.purchases * value) / 100;
            this.spent_percentage = value;
        
            this.mutex = false
          }
        },
        spent_dollar: function(value, old_value) {
          if (!this.mutex) {
            this.mutex = true
            
            this.spent_dollar = value;
            this.spent_percentage = (value / this.vendor.purchases) * 100;
            
            this.mutex = false
          }
        }
      }
    })
ZachB
  • 13,051
  • 4
  • 61
  • 89
Justin MacArthur
  • 4,022
  • 22
  • 27
  • The mutex doesn't really do anything here since there is only one thread and there are no async operations inside each watcher. So once a watcher starts running it will finish executing its code before the other watcher will start, even without the watcher. Side effects (i.e. other watchers) are collected into a list and being executed after this code finishes. – danbars Apr 28 '23 at 06:40