Vue.js - Computed property not updating - child component

时间:2018-12-03 13:09:58

标签: javascript vue.js

I've created a simple component named DefaultButton. It bases on properties, that are being set up whenever this component is being created. The point is that after mounting it, It does not react on changes connected with "defaultbutton", that is an object located in properties

<template>
  <button :class="buttonClass" v-if="isActive" @click="$emit('buttonAction', defaultbutton.id)" >
    {{ this.defaultbutton.text }} 
  </button>
  <button :class="buttonClass" v-else disabled="disabled">
    {{ this.defaultbutton.text }} 
  </button>
</template>


<script>
export default {
  name: "defaultbutton",
  props: {
    defaultbutton: Object
  },
  computed: {
    buttonClass() {
      return `b41ngt ${this.defaultbutton.state}`;
    },
    isActive() {
      return (this.defaultbutton.state === "BUTTON_ACTIVE" || this.defaultbutton.state === "BUTTON_ACTIVE_NOT_CHOSEN");
    }
  }
};
</script>

Having following component as a parent one:

<template>
    <div v-if="state_items.length == 2" class="ui placeholder segment">
        {{ this.state_items[0].state }}
        {{ this.state_items[1].state }}
        {{ this.current_active_state }}
      <div class="ui two column very relaxed stackable grid">
        <div class="column">
            <default-button :defaultbutton="state_items[0]" @buttonAction="changecurrentstate(0)"/>
        </div>
        <div class="middle aligned column">
            <default-button :defaultbutton="state_items[1]" @buttonAction="changecurrentstate(1)"/>
        </div>
      </div>
      <div class="ui vertical divider">
        Or
      </div>
    </div>

</template>


<script type="text/javascript">
    import DefaultButton from '../Button/DefaultButton'

    export default {
        name: 'changestatebox',
        data() {
            return {
                current_active_state: 1
            }
        },
        props: {
            state_items: []
        },
        components: {
            DefaultButton
        },
        methods: {
            changecurrentstate: function(index) {
                if(this.current_active_state != index) {
                    this.state_items[this.current_active_state].state = 'BUTTON_ACTIVE_NOT_CHOSEN';
                    this.state_items[index].state = 'BUTTON_ACTIVE';
                    this.current_active_state = index;
                }
            },
        },
        mounted: function () {
            this.state_items[0].state = 'BUTTON_ACTIVE';
            this.state_items[1].state = 'BUTTON_ACTIVE_NOT_CHOSEN';
        }
    }

</script>

It clearly shows, using:

{{ this.state_items[0].state }}
{{ this.state_items[1].state }}
{{ this.current_active_state }}

that the state of these items are being changed, but I am unable to see any results on the generated "DefaultButtons". Classes of objects included in these components are not being changed.

@edit

I've completely changed the way of delivering the data. Due to this change, I've abandoned the usage of an array; instead I've used two completely not related object. The result is the same - class of the child component's object is not being

DefaulButton.vue:

<template>
  <button :class="buttonClass" v-if="isActive" @click="$emit('buttonAction', defaultbutton.id)" >
    {{ this.defaultbutton.text }} 
  </button>
  <button :class="buttonClass" v-else disabled="disabled">
    {{ this.defaultbutton.text }} 
  </button>
</template>

<style lang="scss">
  import './DefaultButton.css';
</style>

<script>
export default {
  name: "defaultbutton",
  props: {
    defaultbutton: {
      type: Object,
      default: () => ({
        id: '',
        text: '',
        state: '',
      })
    }
  },
  computed: {
    buttonClass() {
      return `b41ngt ${this.defaultbutton.state}`;
    },
    isActive() {
      return (this.defaultbutton.state === "BUTTON_ACTIVE" ||
        this.defaultbutton.state === "BUTTON_ACTIVE_NOT_CHOSEN");
    }
  }
};
</script>

ChangeStateBox.vue:

<template>
    <div class="ui placeholder segment">
        {{ this.state_first.state }}
        {{ this.state_second.state }}
        {{ this.current_active_state }}
      <div class="ui two column very relaxed stackable grid">
        <div class="column">
            <default-button :defaultbutton="state_first" @buttonAction="changecurrentstate(0)"/>
        </div>
        <div class="middle aligned column">
            <default-button :defaultbutton="state_second" @buttonAction="changecurrentstate(1)"/>
        </div>
      </div>
      <div class="ui vertical divider">
        Or
      </div>
    </div>

</template>

<script type="text/javascript">
    import DefaultButton from '../Button/DefaultButton'

    export default {
        name: 'changestatebox',
        data() {
            return {
                current_active_state: 1
            }
        },
        props: {
            state_first: {
                type: Object,
                default: () => ({
                  id: '',
                  text: ''  
                })
            },
            state_second: {
                type: Object,
                default: () => ({
                  id: '',
                  text: ''  
                })
            },
        },
        components: {
            DefaultButton
        },
        methods: {
            changecurrentstate: function(index) {
                if(this.current_active_state != index) {
                    if(this.current_active_state == 1){

                        this.$set(this.state_first, 'state', "BUTTON_ACTIVE_NOT_CHOSEN");
                        this.$set(this.state_second, 'state', "BUTTON_ACTIVE");

                    } else {

                        this.$set(this.state_first, 'state', "BUTTON_ACTIVE");
                        this.$set(this.state_second, 'state', "BUTTON_ACTIVE_NOT_CHOSEN");

                    }
                    this.current_active_state = index;
                }
            },
        },
        created: function () {
            this.state_first.state = 'BUTTON_ACTIVE';
            this.state_second.state = 'BUTTON_ACTIVE_NOT_CHOSEN';
        }
    }
</script>

3 个答案:

答案 0 :(得分:2)

You're declaring props wrong。它是道具名称的数组,或者是一个对象,每个道具都声明一个类型,或者是一个对象,每个道具都声明了多个属性。

你有

    props: {
        state_items: []
    },

但应该supply a default

    props: {
        state_items: {
            type: Array,
            default: []
        }
    },

但是您的问题很可能是以Vue can't react to the change

的方式突变state_items

答案 1 :(得分:1)

您的主要问题是更改按钮状态的方式,根据Array change detection,vue无法通过索引来检测突变。

  

由于JavaScript的限制,Vue无法检测以下内容   更改为数组:

     

直接用索引设置项目时,例如   vm.items[indexOfItem] = newValue当您修改   阵列,例如vm.items.length = newLength

答案 2 :(得分:0)

以防有人遇到相同的问题:

@Roy J和@DobleL是正确的。

此问题背后的原因与状态对象的错误初始化有关。

根据文档:

{
  "docs" : [
    {
      "doc" : {
        "_index" : "_index",
        "_type" : "_type",
        "_id" : "_id",
        "_source" : {
          "other_field" : "other value"
        },
        "_ingest" : {
          "timestamp" : "2018-12-03T16:33:33.885909Z"
        }
      }
    }
  ]
}

在读这句话之前,我先从以下对象开始作为初始数据:

Vue cannot detect property addition or deletion. 
Since Vue performs the getter/setter conversion process during instance
initialization, a property must be present in the 
data object in order for Vue to convert it and make it reactive. 

的正确版本如下:

var local_state_first = {
  id: '1',
  text: 'Realized',
};

var local_state_second = {
    id: '2',
    text: 'Active'  
};

而将主要组件声明为:

var local_state_first = {
  id: '1',
  text: 'Realized',
  state: 'BUTTON_ACTIVE'
};

var local_state_second = {
  id: '2',
  text: 'Active',
  state: 'BUTTON_ACTIVE'
};

其余代码保持不变(请看我主要帖子中的@edit标记)