Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pushing to array doesn't trigger vue's reactivity

I already know that editing object within an array doesn't work for vue.js due to its limited capability and base on what I read using vue.set should solve this problem easily but I am having a hard time making it work.

this is my sample data. The parent object is called resource and inside resource there are power_generator_groups , inside the power_generator_groups are power_generators.

{
  "id": 5,
  "bg_member_id": 1,
  "code": "G0633",
  "name": "namex1",
  "power_generator_groups": [
    {
      "id": 1,
      "resource_id": 5,
      "code": "GC033",
      "power_generators": [
        {
          "id": 1,
          "power_generator_group_id": 1,
          "code": "XXXXX",
          "name": "test",
          "contract_number": "00000003",
          "supply_max": 1000,
        },
        {
          "id": 2,
          "power_generator_group_id": 1,
          "code": "XXXXX",
          "name": "test",
          "contract_number": "00000003",
          "supply_max": 1000,
        },
        {
          "id": 3,
          "power_generator_group_id": 1,
          "code": "XXXXX",
          "name": "test",
          "contract_number": "00000003",
          "supply_max": 1000,
        }
      ]
    },
    {
      "id": 2,
      "resource_id": 5,
      "code": "GC033",
      "contract_number": "065C001",
      "power_generators": [
        {
          "id": 4,
          "power_generator_group_id": 2,
          "code": "XXXXX",
          "name": "test",
          "contract_number": "00000003",
          "supply_max": 1000,
        }
      ]
    }
  ]
}

and this is my vue file

<template lang="pug">
  .wrapper
    .animated.fadeIn
      b-container(fluid='')
        b-row.my-1
          b-col(sm='3')
            label(:for='`type-key`') companyName
          b-col(sm='9')
            b-form-select(v-model='item.bg_member_id' :options="companyList")
          b-col(sm='3')
            label(:for='`type-key`') code
          b-col(sm='9')
            b-form-input(v-model='item.code')

        b-button(size='sm', @click='addPowerGeneratorGroup', variant="primary")
          | Add power_generator_group
        template(v-for='(group, group_index) in item.power_generator_groups')
          b-card(
            header-tag="header"
            footer-tag="footer"
            )
            div(slot="header")
              i.fa.fa-align-justify
              strong power_generator_groups
              button.btn.btn-default.btn-sm(type='button' @click='deletePowerGeneratorGroup(group_index)')
                span.fa.fa-trash-o
            div
            b-row.my-1
              b-col(sm='3')
                label(:for='`type-key`')
                  | code
              b-col(sm='9')
                b-form-input(v-model='group.code')
              b-col(sm='3')
                label(:for='`type-key`') group name
              b-col(sm='9')
                b-form-input(v-model='group.name')
              b-col(sm='3')
                label(:for='`type-key`') contract number
              b-col(sm='9')
                b-form-input(v-model='group.contract_number')
              b-container.bv-example-row(fluid='')
                b-button(size='sm', @click='addPowerGenerator(group_index)', variant="primary")
                  | add power_generator
              b-card(
                header-tag="header"
                footer-tag="footer"
                )
                div(slot="header")
                  i.fa.fa-align-justify
                  strong power_generators
                  b-table(small v-bind:item="group.power_generators" v-bind:fields="fields" fixed responsive)
                    template(v-for="field in fields" :slot="field.key" slot-scope="contract")
                      template(v-if="field.key == 'actions'")
                        b-button(size='sm', @click='deletePowerGenerator(group_index, contract.index)', variant="primary")
                          | Del
                      template(v-else)
                        b-form-input(:type='types[field.key]' v-model = 'contract.item[field.key]')


        b-button(size='sm', @click='save', variant="primary")
          | Save
</template>

<script>
import Vue from 'vue';
  export default {
    props: {
      item: {
        type: Object,
        required: true,
        default: () => {
        }
      },
      companyList: Array
    },
    data() {
      return {
        companyId: null,
        code: null,
        name: null,
        contract_number: null,
        fields: [
          {key: 'actions', label: '' },
          {key: 'code', label: 'code', sortable: true, sortDirection: 'desc'},
          {key: 'name', label: 'name', sortable: true, class: 'text-center'},
          {key: 'contract_number', label: 'contractNo'},
          {key: 'supply_max', label: 'maxunit'}
        ],
        types: {
          code: 'text',
          name: 'text',
          contract_number: 'text',
          supply_max: 'number'
        },
        items: 
        {}
      }
    },
    mounted() {
    },
    computed: {},
    methods: {
    addPowerGenerator(index) {
      console.log(index)
      console.log(this.item)
      const pgg = this.item.power_generator_groups[index];
      const pgs = pgg.power_generators || Vue.set(pgg, 'power_generators', []);
      pgs.push(
        {
          "code": "",
          "name": "",
          "contract_number": "",
          "supply_max": "",
        }
      )
    },
    addPowerGeneratorGroup() {
      const pggs = this.item.power_generator_groups || Vue.set(this.item, 'power_generator_groups', []);
      pggs.push(
        {
          "resource_id": this.item.id,
          "name": "",
          "code": "",
          "contract_number": "",
          "power_generators": []
        }
      )
    },
      deletePowerGenerator(group_index, field_index) {
        this.item.power_generator_groups[group_index].power_generators.splice(field_index, 1);
      },
      deletePowerGeneratorGroup(group_index) {
        this.item.power_generator_groups.splice(group_index, 1);
      },
      save: function () {
        this.$parent.save(this.item)
      }
    }
  }
</script>
like image 933
obliviousfella Avatar asked Oct 15 '22 13:10

obliviousfella


1 Answers

You'll need Vue.set only if you have power_generator_groups that have no power_generators property, since Vue can't detect when you add it otherwise. Assuming there are no problems with your template (you didn't show it), change your addPowerGenerator method:

addPowerGenerator(index) {
  console.log(index)
  console.log(this.item)
  const pgg = this.item.power_generator_groups[index];
  const pgs = pgg.power_generators || Vue.set(pgg, 'power_generators', []);
  pgs.push(
    {
      "code": "",
      "name": "",
      "contract_number": "",
      "supply_max": "",
    }
  )
}

Vue.set returns the property you just created which is how pgs contains the proper reference in that case.

Do the same thing for the new method you edited in:

addPowerGeneratorGroup() {
  const pggs = this.item.power_generator_groups || Vue.set(this.item, 'power_generator_groups', []);
  pggs.push(
    {
      "resource_id": this.item.id,
      "name": "",
      "code": "",
      "contract_number": "",
      "power_generators": []
    }
  )
}

You can see a mockup here where I've assumed this.item refers to the resource itself. I included a 3rd group which has no power_generators property for testing.

like image 123
Dan Avatar answered Nov 15 '22 06:11

Dan