2

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;
    }
  }
});

1 Answer 1

2

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>

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.