Infineight
Infineight

Reputation: 568

JSON and v-if troubles with Vue.js

In the following example neither of the v-if related divs seem to get rendered before or after clicking the Add button. It seems like Vue.js isn't running any updates when the pizzas JSON object is updated.

Is there a solution to this problem without resorting to changing the pizzas variable into being an array?

<div id="app">
  <div v-for="pizza in pizzas">
    {{ pizza }}
  </div>
  <div v-if="totalPizzas === 0">
    No pizza. :(
  </div>
  <div v-if="totalPizzas > 0">
    Finally, some pizza! :D
  </div>
  <button @click="add">Add</button>
</div>
var app = new Vue({
  el: '#app',
  data: {
    pizzas: {}
  },
  methods: {
    add: function() {
      this.pizzas['pepperoni'] = { size: 16, toppings: [ 'pepperoni', 'cheese' ] };
      this.pizzas['meaty madness'] = { size: 14, toppings: [ 'meatballs', 'sausage', 'cajun chicken', 'pepperoni' ] };
    },
    totalPizzas: function() {
      return Object.keys(this.pizzas).length;
    }
  }
});

Upvotes: 2

Views: 293

Answers (1)

raina77ow
raina77ow

Reputation: 106483

There are several things to be improved in your code. Most of them are about syntax. For example, methods should be called, but computed properties can be queried directly: that's why it's @click="add()", but totalPizzas === 0 makes sense only if it's a computed property.

The crucial thing to understand, however, is how reactivity works in VueJS. See, while you change your object innards, adding new properties to it, this change is not detected by VueJS. Quoting the docs:

Vue does not allow dynamically adding new root-level reactive properties to an already created instance. However, it’s possible to add reactive properties to a nested object using the Vue.set(object, key, value) method:

Vue.set(vm.someObject, 'b', 2)

You can also use the vm.$set instance method, which is an alias to the global Vue.set:

this.$set(this.someObject, 'b', 2)

Sometimes you may want to assign a number of properties to an existing object, for example using Object.assign() or _.extend(). However, new properties added to the object will not trigger changes. In such cases, create a fresh object with properties from both the original object and the mixin object:

// instead of `Object.assign(this.someObject, { a: 1, b: 2 })`
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })

And this is how it might work:

var app = new Vue({
  el: '#app',
  data: {
    pizzas: {}
  },
  computed: {
    totalPizzas: function() {
      return Object.keys(this.pizzas).length;
    }
  },
  methods: {
    add: function() {
      this.pizzas = Object.assign({}, this.pizzas, {
        pepperoni: { size: 16, toppings: [ 'pepperoni', 'cheese' ] },
        ['meaty madness']: { size: 14, toppings: [ 'meatballs', 'sausage', 'cajun chicken', 'pepperoni' ] }
      });
    },
  }
});
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<div id="app">
  <div v-for="pizza in pizzas">
    Size: {{ pizza.size }} inches
    Toppings: {{ pizza.toppings.join(' and ') }}
  </div>
  <div v-if="totalPizzas === 0">
    No pizza. :(
  </div>
  <div v-if="totalPizzas > 0">
    Finally, some pizza! :D
  </div>
  <button @click="add()">Add</button>
</div>

Upvotes: 2

Related Questions