Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

rokkan's avatar

How to create Computed property of sub object

I'm trying to get the total of a sub object and I want to set that total to a variable of the object:

The total (computed property) is being assigned to the root data.total, I want it to be assigned to data.cart.total

js fiddle at end

<div id="app">

  <cart :cart.sync="cart" inline-template>
    Ex A: {{total}}  <!-- works -->
    </br>
    Ex B: {{cart.total}}     <!-- doesnt work -->
    <ul>
      <li v-for="product in cart.products">
        <h3>{{product.name}}</h3>
        <i>{{product.price}}</i>
      </li>
    </ul>
  </cart>

</div>

Vue.component('cart', {
  props: ['cart'],
  computed: {
    total: function() {
        return this.cart.products.reduce((a, b) => a + b.price, 0);
    }
  }
});

new Vue({
  el: '#app',
  data: {
    cart: {
      products: [
        {
          name: 'Hello',
          price: 19.99
        }
      ]
    },
  }
});

jsfiddle: https://jsfiddle.net/yfn1ggr0/1/

0 likes
8 replies
davestewart's avatar

It's looking a bit confused, in the way that you have 3 "cart"s:

  • 1 component
  • 1 component computed property
  • 1 data property

In your cart component:

  • total will point to the component's (computed) total, which is why it works.
  • cart.total points to the passed prop cart which looks like a normal JS Object, stored in data which has a property products but no total property (computed or otherwise)

I'd suggest:

  • renaming all your objects so you only have one of each
  • using a shared store object, perhaps ProductStore and passing that around

You can make a store object either as a POJO (plain old JavaScript Object) which won't have access to computed properties, or as a Vue instance, which will - so then you can have all properties in one place, and do something like: store.products and store.total or even store.getTotal()

rokkan's avatar

I understand why it works. The part I dont understand is when creating a computed property, how can you set it to a sub object as in cart or etc. I want to call total from like store.total or cart.total. But when creating a computed property I dont know how to set that to the object.

Example:

computed: {
    cart.total: function() {
        return this.cart.products.reduce((a, b) => a + b.price, 0);
    }
  }

doesnt work...

my question is how can I control where the computed property gets set. It seems to always get set at the data root...

davestewart's avatar

A computed property is created on whatever object you want. In your case, it looks like the cart component, so you just need to make sure your references are correct.

However, I've just noticed you're using an inline template, so your cart reference is data whilst your total reference is the component.

Add <pre>{{ cart | json }}</pre> where you have Ex B. and you will see.

Even Evan says it's best to avoid inline templates...

However, inline-template makes the scope of your templates harder to reason about, and makes the component’s template compilation un-cachable. As a best practice, prefer defining templates inside the component using the template option.

... so consider a setup like this:

leolam2005's avatar

template:

<span>{{ errors.title }}</span>

Computed props:

        computed : {

          errors() {
            return { // I even injected a custom Validator Class ;P
              title : (new Validator('min:3|max:50')).check(this.fields.title).errors,
              slug : (new Validator('min:3|max:20')).check(this.fields.slug).errors,
              target : (new Validator('min:2000|max:200000|number')).check(this.fields.target).errors,
              body : (new Validator('min:3|max:1000')).check(this.fields.body).errors,
            }
          }, ...

You are welcome :P

rokkan's avatar

@leolam2005

Your solution works but doesn't feel ideal. In my data I have a cart object with products array. I want to also add a computed value total to the cart to live as a sibling to the products array. Your solution is me creating a new object, i want to have 1 source of data not 2...

Goal:

    cart: {
        total: XX, <--computed
        products: [
            {
                name: "product 1",
                price: 10,
            },
            {
                name: "product 2",
                price: 12,
            },
        ],
    }
leolam2005's avatar
Level 11

OK, interesting, so you want to either put

products (data) plus total (computed) into computed property or

put the products (data) plus total (computed) into data?

Total is Computed value, so put in computed property.

products are Data ( or property) so put in Data,

what's not ideal?

well, yes you can, and don't forget to add watch on cart.products

new Vue({
  el: '#app',
  data: {
        cart: {
        total: 0,
        products: [
          {
            name: 'Hello',
            price: 19.99
          },
          {
            name: 'Hello',
            price: 19.99
          }
        ]
     }
  },
  watch: {
    'cart.products' : function(oldVal, newVal) {
        this.cart.total = this.cart.products.reduce((a, b) => a + b.price, 0);
    }
  },
  ready() {
    this.cart.total = this.cart.products.reduce((a, b) => a + b.price, 0);
    this.cart.products.push({name: 'Hello',price: 19.99});
    this.cart.products.push({name: 'Hello',price: 19.99});
    this.cart.products.push({name: 'Hello',price: 19.99});
  }
});

I code in your "ideal" way, You are welcome ;p

Oh god, I messed up the entire MVVM pattern.. =O

1 like
rokkan's avatar

@leolam2005 Thank you. The part i felt wasnt ideal was creating the new object to contain cart and total in your first solution.

leolam2005's avatar

@rokkan

One more thing, you should modify the watch function, currently the watch function may lead to loop many times(though not infinite).

Normally, we put computed property and data differently because they serve their different purposes in MVVM model.

I strongly suggest putting total in computed and carts in props / data, no matter how it feels to you.

I think when you write Cart Component, then call Cart component's "Total" computed property as {{ Cart.total }} would be very ideal, it really doesn't matter where it is put.

The first example I gave is just a demo on how you can twist to play with computed value.

Have Fun with VueJS :)

Please or to participate in this conversation.