推送到数组不会触发 vue 的反应性

Posted

技术标签:

【中文标题】推送到数组不会触发 vue 的反应性【英文标题】:Pushing to array doesn't trigger vue's reactivity 【发布时间】:2019-11-20 16:26:16 【问题描述】:

我已经知道在数组中编辑对象不适用于 vue.js,因为它的功能有限,并且基于我使用 vue.set 阅读的内容应该很容易解决这个问题,但我很难让它工作.

这是我的示例数据。父对象称为resource,resource里面有power_generator_groups,power_generator_groups里面有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,
        
      ]
    
  ]

这是我的 vue 文件

<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>

【问题讨论】:

我猜是因为数组在内存中的地址在推送时没有变化,所以没有检测到变化。 解决这个问题的方法是什么?我试过 vue.set 但没有运气 【参考方案1】:

只有当 power_generator_groups 没有 power_generators 属性时,您才需要 Vue.set,因为 Vue 无法检测到您何时添加它。 假设您的模板没有问题(您没有显示它),请更改您的 addPowerGenerator 方法:

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 返回您刚刚创建的属性,这就是pgs 在这种情况下包含正确引用的方式。

对你编辑的新方法做同样的事情:

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 我假设this.item 指的是资源本身。我包括了第三组,它没有用于测试的 power_generators 属性。

【讨论】:

仍然对我不起作用,我认为这会起作用,但我认为问题出在我的其他方法上。我更新了问题,你可以检查一下吗? @obliviousfella - 如果您仔细阅读我的答案,您会发现它也解决了您刚刚发布的新方法的问题。您需要以同样的方式使用Vue.set。我更新了模型给你看。 我尝试用你的 jsfiddle 替换我的代码,但我似乎无法让它工作,该数组在 console.log 中得到更新,但它只是不反映我的 UI 中的变化。也许我的模板有问题? 应该是,不然其他代码某处有错误。 因为它帮助我找到了问题,我会投票赞成,谢谢你的时间。

以上是关于推送到数组不会触发 vue 的反应性的主要内容,如果未能解决你的问题,请参考以下文章

限制 vue/vuex 反应性

推送到数组时反应状态无法正确更新

节点 js 闭包需要推送到数组

如何在 Vue 中求和并推送到匹配的对象

为啥 Angular 2 ngOnChanges 不响应输入数组推送

React JS将项目推送到数组以获取列表