VueJS 2 - Monitoring variables within arrays and objects

Illustration of the issue: https://jsfiddle.net/hxv5bLqt/

In my data structure, there are two levels (arrays). The top level represents "parts", and each "part" contains a set of "items". Additionally, there is a variable that stores the total value of all items. The functionality needed involves recalculating this total whenever a user modifies any value field.

Although I have implemented a solution for this requirement, I find it unsatisfactory as it involves manually tracking where the "this.recompute()" method should be called. Utilizing watch functions or computed properties would offer a cleaner and more reactive approach.

Unfortunately, implementing a "watch" or a "computed property" to monitor changes within objects nested in arrays has proven challenging.

An ideal watch function that I envisioned working would look something like:

watch: {
    'parts.X.items.Y.value': function(oldValue, newValue) {
         this.recompute()
    }
}

The variables "X" and "Y" represent placeholders for indicating any positions within the arrays that we want to observe.

This setup would trigger the watch every time a user interacts with an item by removing it or editing its value. However, this approach does not yield the expected results, leading me back to manually calling "this.recompute()" at specific points in the code:

onClickRemoveItem(event, part, item) {
    ...
    this.recompute() // RECOMPUTE HERE.
},

onKeyupItemValue(event) {
    this.recompute() // RECOMPUTE HERE.
},

I believe relying on explicit function calls like this is not an elegant solution, especially when dealing with numerous potential call sites for "this.recompute()". Is there a more efficient and elegant way to address this issue?

Answer №1

When you create a computed property for your total, you automatically gain more reactivity without the need to manually track updates or recalculate values. For a specific implementation example, check out this answer: https://jsfiddle.net/16wk9grf/

Here is an example of how it can be implemented:

<script src="https://unpkg.com/vue"></script>
<div id="app">
  <h3>Tests</h3>
  <ul>
    <li v-for="part in parts">
      <span>{{ part.name }}</span>
      <ul>
        <li v-for="item in part.items">
          R$ <input type="number" v-model="item.value">
          <button type="button" @click="onClickRemoveItem($event, part, item)">Remove</button>
        </li>
        <p>
           Part subtotal: ${{ part.items.reduce((acc, item) => acc + parseFloat(item.value), 0) }}
        </p>
        <p>
          Part subtotal (alternate): ${{ calculatePartSubtotal(part.items) }}
      </p>
      </ul>
    </li>
  </ul;

  <p><strong>Total value:</strong>R$ {{ total }}</p>
</div>

JS

new Vue({
  el: '#app',
  data() {
    return {
      // Mock nested data.
      parts: [
        { id: 1, name: 'Part 1', items: [{ id: 1, value: 10.00 }, { id: 2, value: 5.00 }] },
        { id: 2, name: 'Part 2', items: [{ id: 3, value: 15.00 }] }
      ]
   }
 },
computed: {
  total () {
    let total = 0.0
    for (let i = 0; i < this.parts.length; i++) {
      for (let j = 0; j < this.parts[i].items.length;; j++) {
        total += parseFloat(this.parts[i].items[j].value)
      }
    }
    return total
  }
},
methods: {
  calculatePartSubtotal (items) {
    return items.reduce((acc, item) => acc + parseFloat(item.value), 0)
  },
  onClickRemoveItem(event, part, item) {
    let index = part.items.findIndex(e => e.id === item.id)
    this.$delete(part.items, index)
   }
 }
})

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Troubleshooting an issue with window.close in AngularJS

I'm struggling to properly execute the window.close function without encountering the error message below: Scripts may close only the windows that were opened by it. Here is the structure of my application: HTML: <html ng-app="mynewAPP" ng-co ...

Using webpack's hash in JavaScript for cache busting

Can someone guide me on adding a hash to my js file name for cache-busting purposes? I've researched online but still uncertain about where to include the [hash] element. Any help would be appreciated. Provided below is a snippet from my webpack.conf ...

ESlint is unable to parse typescript in .vue files

After using vue ui to build my project, here is the content of my package.json: "@vue/cli-plugin-eslint": "^4.1.0", "@vue/cli-plugin-typescript": "^4.1.0", "@vue/eslint-config-standard": "^4.0.0", "@vue/eslint-config-typescript": "^4.0.0", "eslint": "^5.1 ...

The hamburger icon seems to be frozen and unresponsive

I'm struggling to get my hamburger icon to reveal the navigation bar when clicked. The icon itself functions properly and adapts to different screen sizes, but the overlay remains stationary. Check out my code on JSFiddle! <html> <head> ...

"The integration of Laravel Passport and Vue, together with Guzzle, is failing to return the token

Utilizing Laravel Passport for my oauth2 login setup, I encountered an issue when trying to deploy my application on DigitalOcean after successful testing on localhost. Following tutorials by Dre Himself, I set up nginx, .env file, composer, npm, passport ...

Ajax RSS Feed - Weather Data from OpenWeatherMap

I am encountering an issue where the feed does not refresh with new news as they come in, even after selecting an option. This same problem is also occurring with openweather. I suspect that everything is being cached when it shouldn't be. Should I ch ...

Issue: "StoreController Undefined" error in Python Flask + Angular application

In the python flask application that I have built with Angular JS for the front end, there are three main files. app.py import json import flask import numpy as np app = flask.Flask(__name__) @app.route("/") def index(): ...

Leveraging yield in Angular (ES6)

I have been experimenting with ES6 and attempting to use yield in conjunction with an angular request. However, I am encountering some unexpected behavior. When I write var data = yield getData();, the output is not what I had anticipated. Instead of recei ...

What steps can be taken to disable Angular's automatic trimming of fields?

Is there a global way to prevent Angular from automatically trimming input fields throughout the entire application? I know that I can avoid it for specific fields using the ngTrim directive, but it's not ideal to add this directive to every text fiel ...

Is there a way in vee-validate to validate specific sections of a form?

I'm struggling with my English skills. When I utilize 'this.$validator.validate()', I am seeking a way to only validate specific inputs on the page. Is there a method to achieve this? ...

The integration of Firebase Google Auth seems to encounter issues when used on the Vercel

My experience with Vercel deployment has been quite interesting. While I find that Google authentication works seamlessly on localhost, it seems to encounter an issue when deployed on Vercel. Specifically, after logging out, the currentUser variable always ...

Navigation bar failing to show all content on Safari web browser

When navigating to this website, http://www.togethermutualinsurance.co.uk, using any browser except for Safari on Windows, the menu displays correctly. However, in Safari, the title of the menus and submenus with images does not display properly. Despite ...

How can I create a customized placeholder effect for the Paytm login page text box?

Looking to create a placeholder effect on the text boxes for a Paytm login page? https://i.sstatic.net/sox0a.png ...

Guide to implementing an animate-on-scroll feature for data retrieved in a React application

Background: I am trying to implement an AOS-effect on multiple divs that are populated with data from a server and rendered after the initial load: import {useEffect, useState} from "react"; export const Test = () => { const [data, setData] = u ...

Launching various modals on marker click

I'm having an issue where I need a different modal to be displayed depending on the name in the markerSet array. Currently, the if/else statement is always returning the same modal. Take a look at the if statement in my JavaScript code below. The nam ...

Differences in disabled option value selection between jQuery legacy and updated versions

Recently, I upgraded the jQuery version in my project from 1.7.2 to 2.1.1 and included jquery migrate for fallback support. During testing, I encountered an issue with the value of a select element in jQuery versions 2.1.1 and 1.7.2: For version 1.7.2, t ...

What could be causing the Angular HTTPClient to make duplicate REST calls in this scenario?

I am encountering an issue with my Angular service that consumes a Rest API. Upon inspecting the network and the backend, I noticed that the API is being called twice every time: Here is a snippet of my Service code: getAllUsers():Observable<any>{ ...

Accessing the current instance in Vue when triggered by a checkbox event

Recently, I tested out a to-do-list application built in Vue that has a checkbox feature to mark completed items. I'm looking for a way to access the current instance from the checkbox event. Here's what I've accomplished so far: myObject ...

Change the background color of form checkboxes in a different way

I'm currently working on a form design and I would like to create a feature where the background color of each option alternates. This is similar to how you can style alternating rows in tables using CSS tr:nth-child(even){background-color: #f2f2f2;} ...

What are the steps to recursively transform a JavaScript object?

I am currently working on recursively transforming a JavaScript object, but I have encountered an issue. The problem is: if any object key has exactly two properties - 'label' and 'value', then the value should change to the label only ...