4

The Problem

I have a "products" array with multiple objects. Each product object contains the property "price". I want to watch this property in each product for possible changes. I am using this to calculate a commission price when the user changes the price in an input box.

My products array looks like this;

[
  0: {
    name: ...,
    price: ...,
    commission: ...,
  },
  1: {
    name: ...,
    price: ...,
    commission: ...,
  },
  2: {
    name: ...,
    price: ...,
    commission: ...,
  },
  ...
  ...
  ...
]

My code

I've tried this, but it doesn't detect any changes except for when the products are first loaded;

    watch  : {
        // Watch for changes in the product price, in order to calculate final price with commission
        'products.price': {
            handler: function (after, before) {
                console.log('The price changed!');
            },
            deep   : true
        }
    },

The products are loaded like this;

mounted: async function () {
            this.products = await this.apiRequest('event/1/products').then(function (products) {
                // Attach reactive properties 'delete' & 'chosen' to all products so these can be toggled in real time
                for (let product of products) {
                    console.log(product.absorb);
                    Vue.set(product, 'delete', false);
                    Vue.set(product, 'chosen', product.absorb);
                }

                console.log(products);

                return products;
            })
        }

Other questions I've looked at Vue.js watching deep properties This one is trying to watch a property that does not yet exist. VueJs watching deep changes in object This one is watching for changes in another component.

1 Answer 1

2

You can't really deep-watch products.price, because price is a property of individual product, not the products array.

Declarative watchers are problematic with arrays, if you attempt to use an index in the watch expression, e.g products[0].price, you get a warning from Vue

[Vue warn]: Failed watching path: “products[0].price”. Watcher only accepts simple dot-delimited paths. For full control, use a function instead.

What this means is you can use a programmatic watch with a function, but it's not explained all that well.

Here is one way to do it in your scenario

<script>
export default {
  name: "Products",
  data() {
    return {
      products: []
    };
  },
  mounted: async function() {
    this.products = await this.apiRequest('event/1/products')...

    console.log("After assigning to this.products", this.products);

    // Add watchers here, using a common handler
    this.products.forEach(p => this.$watch(() => p.price, this.onPriceChanged) );

    // Simulate a change
    setTimeout(() => {
      console.log("Changing price");
      this.products[0].price= 100;
    }, 1000);
  },
  methods: {
    onPriceChanged(after, before) {
      console.log(before, after);
    }
  }
};
</script>

Here is my test Codesandbox (I use color instead of price because there's no price in the test api)

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

1 Comment

Nice, this seems to do what I need it to do - thank you!

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.