[Vuejs]-Vue.js auto update computed

0👍

Welcome to Stack Overflow. I’ve made an attempt at fixing your code, tbh I found it really difficult to understand and I think you’re overusing computed properties, e.g.

items: function() {
    return this.obj
},

You might as well just reference this.obj instead of this.items as you’re over complicating your code.

Your main problem was initialising the data, Since Vue doesn’t allow dynamically adding root-level reactive properties, you have to initialize Vue instances by declaring all root-level reactive data properties upfront, even with an empty value Declaring Reactive Properties

So I initialised s like this:

let s = [{
  subitems: [{
    eprice: "0",
    factor: 0,
    discount: 0,
    qty: 0,
    dprice: 0,
    tp1: 0,
    margin: 0,
    tp2: 0
  }]
}];
let s = [{
  subitems: [{
    eprice: "0",
    factor: 0,
    discount: 0,
    qty: 0,
    dprice: 0,
    tp1: 0,
    margin: 0,
    tp2: 0
  }]
}];
Vue.component('subitem-row', {
  props: ['subitem', 'crt', 'si'],
  template: `
                    <tr>
                        <td>
                            <div class="form-group" v-if="crt == si">
                                <label>EPE</label>
                                <input v-model="subitem.eprice" @change="calculateSubitem();">
                            </div>
                            <span v-else>{{subitem.eprice}}</span>
                        </td>
                        <td>
                            <div class="form-group" v-if="crt == si" @change="parseExpresion(); calculateSubitem();">
                                <label>Anzahl</label>
                                <input v-model="subitem.qtytext">
                            </div>
                            <span v-else>{{subitem.qtytext}}</span>
                        </td>
                        <td>
                            <div class="form-group" v-if="crt == si">
                                <label>Faktor</label>
                                <input v-model="subitem.factor" @change="calculateSubitem();">
                            </div>
                            <span v-else>{{subitem.factor}}</span>
                        </td>
                        <td>
                            <div class="form-group" v-if="crt == si">
                                <label>EK</label>
                                <input v-model="subitem.tp1" readonly>
                            </div>
                            <span v-else>{{subitem.tp1}}</span>
                        </td>
                        <td>
                            <div class="form-group" v-if="crt == si">
                                <label>Marge</label>
                                <input v-model="subitem.margin" readonly>
                            </div>
                            <span v-else>{{subitem.margin}}</span>
                        </td>
                        <td>
                            <div class="form-group" v-if="crt == si">
                                <label>VK</label>
                                <input v-model="subitem.tp2" readonly>
                            </div>
                            <span v-else>{{subitem.tp2}}</span>
                        </td>
                        <td>
                            <div class="form-group" v-if="crt == si">
                                <label>Rabatt</label>
                                <input v-model="subitem.discount" @change="calculateSubitem();">
                            </div>
                            <span v-else>{{subitem.discount}}</span>
                        </td>
                    </tr>
                `,
  methods: {
    calculateSubitem: function() {
      if (this.subitem.hasOwnProperty('eprice') && !isNaN(this.subitem.eprice)) {
        if (!this.subitem.hasOwnProperty('factor') || isNaN(this.subitem.factor))
          this.subitem.factor = 1
        if (!this.subitem.hasOwnProperty('discount') || isNaN(this.subitem.discount))
          this.subitem.discount = 0
        if (!this.subitem.hasOwnProperty('qty') || isNaN(this.subitem.qty))
          this.subitem.qty = 0

        let discount = 1 - (parseFloat(this.subitem.discount.toString().split(',').join('.')) / 100),
          margin = 0
        this.subitem.dprice = (this.subitem.eprice.split(',').join('.') * discount)
        this.subitem.tp1 = (this.subitem.dprice * this.subitem.qty * this.subitem.factor)
        this.subitem.margin = (this.subitem.tp1 * (parseFloat(margin) / 100))
        this.subitem.tp2 = (this.subitem.tp1 + this.subitem.margin)
        this.$forceUpdate() //{TODO} - find an alternative to $forceUpdate
      }
    },
    parseExpresion: function() {
      this.subitem.qty = parseFloat(this.subitem.qtytext.split(',').join('.')) || 0
      this.$nextTick(function() {
        this.calculateSubitem()
      })
    }
  },
})

Vue.component('subitem-row-sum', {
  props: ['sisum'],
  template: `
                    <tr>
                        <td colspan="3">SUM</td>
                        <td>
                            <span>{{sisum.tp1}}</span>
                        </td>
                        <td>
                            <span>{{sisum.margin}}</span>
                        </td>
                        <td>
                            <span>{{sisum.tp2}}</span>
                        </td>
                        <td></td>
                    </tr>
                `,
})

Vue.component('html-textarea', {
  template: `<div class="html-textarea" contenteditable="true" @input="updateHTML" rows="3"></div>`,
  props: ['value'],
  mounted: function() {
    this.$el.innerHTML = this.value;
  },
  methods: {
    updateHTML: function(e) {
      this.$emit('input', e.target.innerHTML)
    }
  }
})

const app = new Vue({
  el: '#app',
  data: {
    obj: s, // main object for loading
    ii: 0, // items index
    si: 0, // subitems index
  },
  computed: {
    items: function() {
      return this.obj
    },
    row: function() {
      if (!this.items.length)
        this.items.push({})
      return this.items[this.ii]
    },
    subitems: function() {
      return this.row.subitems
    },
    srow: function() {
      if (!this.subitems.length)
        this.subitems.push({})
      return this.subitems[this.si]
    },
    sisum: function() {
      debugger;
      let sisum = {
        tp1: 0,
        tp2: 0,
        margin: 0
      }


      this.subitems.forEach(si => {
        sisum.tp1 += si.tp1 || 0
        sisum.tp2 += si.tp2 || 0
        sisum.margin += si.margin || 0
      })
      return sisum;
    }
  },
  methods: {
    setIi: function(i) {
      this.ii = i
    },
    setSi: function(i) {
      this.si = i
    },
    addItemRow: function() {
      this.items.push({})
      this.setIi(this.items.length - 1)
      this.$nextTick(function() {
        document.getElementById('items').scrollIntoView({
          behavior: "smooth",
          block: "end",
          inline: "nearest"
        })
      })
    },
    addSubitemRow: function() {
      this.subitems.push({})
      this.setSi(this.subitems.length - 1)
      this.$nextTick(function() {
        document.getElementById('subitems').scrollIntoView({
          behavior: "smooth",
          block: "end",
          inline: "nearest"
        })
      })
    },
  },
})
body {
  padding: 0;
  margin: 0;
  font-size: 14px;
  font-family: 'Courier New', monotype;
}

.tables-container {
  display: flex;
  flex-direction: column;
  height: 100vh;
}

.table-container {
  height: calc(50% - 2px);
  overflow: auto;
  margin: 0 0 30px 0;
  background: #eee
}

.table-container td {
  padding: 3px 6px;
  width: 60px;
  height: 30px;
  border-bottom: 1px solid black;
  vertical-align: middle;
  font-size: 1.1rem;
}

.html-textarea-container {
  position: relative;
}

.html-textarea {
  background: white;
  border: 1px solid black;
}

.btn-group-edit {
  position: absolute;
  right: 2px;
  bottom: 2px;
  /*display: none;*/
  z-index: 1;
}


/*.html-textarea:hover + .btn-group-edit,
            .html-textarea:focus + .btn-group-edit {
                display: block;
            }*/

.table-container .form-group>input,
.table-container .form-group>textarea,
.table-container .form-group>select,
.html-textarea {
  width: calc(200px - 12px);
  height: calc(30px - 6px);
  font-size: 1.1rem;
  font-family: 'Courier New', monotype;
  word-wrap: break-word;
}

.table-container .form-group>textarea,
.html-textarea {
  width: calc(600px - 12px);
}


/*.table-container .form-group > textarea:focus,
            .html-textarea {
                height: calc(150px - 6px);
            }*/

.btn-add {
  width: 30px;
  height: 30px;
  padding: 4px 0;
}
<div id="app">
  <div class="tables-container">
    <div class="table-container table-subitems">
      <table id="subitems">
        <tr is="subitem-row" v-for="(subitem, i) in subitems" v-bind:subitem="subitem" v-bind:key="i" v-bind:crt="i" v-bind:si="si" v-on:click.native="setSi(i)"></tr>
        <tr is="subitem-row-sum" v-bind:sisum="sisum"></tr>
      </table>
    </div>
    <button class="btn-add" v-on:click="addSubitemRow">+</button>
  </div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

Leave a comment