Navigating Vue and Vuex: Managing interdependent computed values

My unique application is a simplified spreadsheet tool created using Vue and Vuex. It consists of three main components: TableCollection, Table, and Row. The TableCollection contains an array of multiple Table objects, each with its own array of multiple Row objects.

Within the Row component, there is a property called calculatedField which combines two fields within the row to generate a third field. Now, I'm contemplating whether to set up this calculatedField as a computed property within the local scope of the Row component or as a getter within the Vuex store.

The Table component necessitates a certain value named subTotal, calculated by aggregating all the calculatedField values from the rows in the table. Hence, the computation of subTotal directly relies on the calculation of calculatedField.

If I opt for the former approach, implementing calculatedField locally as a computed property of Row ensures its caching. However, encountering difficulties accessing this calculated field from a parent such as Table impedes smooth functionality.

computed : {
    subTotal : function () {
        let total = 0;
        this.table.rows.forEach(function (row) {
           total += row.calculatedField;
        });
        return total;
    }
}

This resulted in NaN, highlighting the challenge of linking calculatedField across components efficiently without duplicating codes in computed properties - violating the DRY principle.

An alternative could involve transforming both subTotal and calculatedField into getters within the store, but introduces complexities like passing arguments to the getter for efficient retrieval, thereby compromising cache optimization.

Lastly, one can consider centralizing the logic for calculatedField in a global helper or mixin to avoid duplication and inefficiencies associated with getters, although departing slightly from compartmentalization specific to Table and Row components.

Amid these considerations, it remains pivotal to explore missed solutions while adhering to the preferred 'Vue-way' of streamlining data flow between interdependent components.

Answer №1

For optimal performance and importance of caching, one strategy is to introduce caching at the Table component level.

In the Row component, emit the new value so the parent component can store it in the cache.

  computed: {
    calculatedField() {
      const result = this.data.field + this.data.other;
      this.$emit('change', this.data.id, result);
      return result;
    }
  },

In the Table component, handle the change event and cache the updated values.

  data() {
    return { cache: {} };
  },

  computed: {
    subTotal() {
      return Object.values(this.cache).reduce((total, value) => total + value, 0);
    }
  },

  methods: {
    onChange(rowId, val) {
      // Ensure reactivity by using $set
      this.$set(this.cache, rowId, val);
    }
  },

When a Row's data changes, a change event is triggered with the new calculated value, which is then stored in the Table's cache for maintaining the subtotal.

The example below illustrates how computed properties are only recalculated when necessary, optimizing performance by refreshing relevant computed properties upon row changes (triggered by clicking the Rand button).

const MyRow = {
  props: {
    data: {
      type: Object
    }
  },
  
  computed: {
    calculatedField() {
      console.log("Calculated field for row", this.data.id);
      const result = this.data.field + this.data.other;
      this.$emit('change', this.data.id, result);
      return result;

    }
  },
  
  methods: {
    onClick() {
      this.data.other = Math.floor(Math.random() * 10);
    }

  },

  template: `
    <tr>
        <td>{{ data.field }}</td>
        <td>{{ data.other }}</td>
        <td>{{ calculatedField }}</td>
        <td><button type="button" @click="onClick">Rand</button></td>
    </tr>
  `
};

const MyTable = {
  props: {
    rows: {
      type: Array
    }
    
  },

  components: {
    MyRow
  },

  data() {
    return {
      cache: {}
    }

  },

  computed: {
    subTotal() {
      console.log("Subtotal of Table");
      return Object.values(this.cache).reduce((total, value) => total + value, 0);

    }
  },

  methods: {
    onChange(rowId, val) {
      console.log("OnChange Event", rowId, val);
      this.$set(this.cache, rowId, val);
      
    }
  },


  template: `
    <div>
        <table border="1">
            <tr><th>field</th><th>other</th><th>calculated</th><th></th></tr>
            <my-row v-for="row in rows" @change="onChange" :key="row.id" :data="row"></my-row>
        </table>
        Subtotal: {{ subTotal }}
    </div>
  `
};

var app = new Vue({
  el: '#app',
  components: {
    MyTable
  },
  
  data: {
    rows: [{
        id: 1,
        field: 1,
        other: 1
      },
      {
        id: 2,
        field: 2,
        other: 2
      },
      {
        id: 3,
        field: 3,
        other: 3
      },
    ]
  },

  template: `<my-table :rows="rows"></my-table>`
});
<div id="app"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>

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

What is the origin of function parameters in javascript?

I have implemented the following code: handleOwnerMode = ownerChecked => { this.setState(prev => ({ ownerChecked, showOwner: !prev.showOwner})) // this.setState(prev => ({ ownerChecked: !prev.ownerChecked, showOwner: !prev.showOwner ...

Angular - Incorporating Query Parameters into URL

For instance, let's say I have the following URL: http://local.com/. When I invoke the function in my SearchController, I aim to set text=searchtext and generate a URL like this: http://local.com/?text=searchtext. Is there a way to achieve this? I at ...

Question about jQuery's XHR

Currently working on a basic jQuery script to parse JSON data and format it. However, I keep encountering an error in Chrome that says: Resource interpreted as Script but transferred with MIME type text/html. After researching on SO, it seems to be a comm ...

Employing Vue's test-utils along with jest to test the accuracy of a particular input element's value

When using Vue test-utils, I am trying to test the value of a specific input element that is populated through a prop. I have managed to locate the element by its id: it("Address should render Street 1", () => { const street1 = wrapper. ...

The necessary parameters are not provided for [Route: sender] with the [URI: {name}/{code}]. The missing parameters are name and code

I have encountered a problem while using AJAX and I am struggling to find a solution. Routes: web.php Route::get('/{name}/{code}', 'TestController@counter'); Route::post('/{name}/{code}', 'TestController@sender')-&g ...

Updating the DOM does not occur by simply adding an object to the Array; instead, the database is updated once the data has

My database has verified data that is being updated, however, the DOM is not reflecting these updates. <ul> <li ng-repeat="aReview in reviewList"> .... .... </li> </ul> <script> if(globalMethods.stringVa ...

Discovering the difference between a singular array and an array of arrays

x = [1, 2,3, 5]; y = [1, [2], [3, [[4]]],[5,6]])); I am currently facing a challenge in finding the difference between these two arrays. function findArrayDifference(arr1, arr2) { var tempArr = [], difference = []; for (var i = 0; i < arr1.l ...

Iterating through each object in the JSON file to showcase details about planes

Question How do I set up a functional example of these airplanes? Background I seem to have the initial part working, indicating that my loop may be causing issues. While I can extract the number of planes, I'm facing difficulties displaying all th ...

Leveraging grunt-develop

I have recently developed a basic NodeJS + Express application that runs smoothly when I use the command node app.js. However, my current task is to incorporate grunt-develop into my project. Here is how I configured it: grunt.initConfig({ develop: { ...

Passing data into a different controller

In the process of building a system that involves selecting elements from a list and viewing their details, I have encountered an issue while using MEAN stack. My goal is to pass the id of the selected element to the controller of the second page. However, ...

Unusual quirks in javascript variables when used with arrays

Unsure if this question has been asked previously. Nevertheless, I couldn't find it anywhere. I've observed a peculiar behavior that seems to occur only with arrays. Here is the typical behavior I anticipate from variables: var k = 10, m = ...

Navigating to a targeted class element using vue.js

In my current setup with Laravel 7, Vue.js 2, and Bootstrap 4, I have a scenario where upon clicking a form, I want the view to scroll to a specific element on the page identified by a class when the submission is successful. The reason for this is to ensu ...

Tips for implementing a wait time for individual items in bee-queue

I've encountered an issue with the delayUntil function in the bee-queue library when creating a queue. await queue .createJob(process) .timeout(60 * 1000 * 2) .retries(2) .backoff('fixed', 60 * 1000) ...

What is the best way to replace HttpClient in Aurelia?

I am completely new to Aurelia. How can I modify the code below in order to implement a dummy HttpClient, such as a json reader, that will provide a static set of json data without the need for a server during development? import {inject} from 'aure ...

Is it necessary to remove a Javascript Event Listener before a VueJS element is unmounted?

I have a straightforward vuejs directive that attaches an event listener to the DOM element upon its mounting. I'm questioning whether it's necessary to detach this event listener before the element is unmounted later on. Despite the fact that th ...

What is the destination for next() in Express js?

I'm new to javascript, nodejs, and express, and facing confusion with the usage of next(). I am trying to make my code progress to the next router using next(), but it seems to be moving to the next then instead. This is what my code looks like: // ...

Obtain Attribute Value Using JQuery

I am trying to extract the value from this attribute element, but I am unsure of the correct way to do it. Can someone help me out? Here is the code snippet: <select name="Hello/1.0/TestWorld" size="1" disabled="disabled" ...

Only carry out a redirect to the specified page if the successRedirect is present in the passport.authenticate function

Encountering some difficulties with a Node Express app. After removing the successRedirect property in the auth method by passport, it fails to redirect. The below code does not redirect to the desired page when the successRedirect is removed and replaced ...

Just a quick inquiry regarding adding new line characters in JSON to be used in

After encountering an issue with a JSON file in my JavaScript application where it would not print new lines when viewed on the console, I am at a loss for a solution. The contents of my JSON file are as follows: [ { "id": "71046" ...

Display a span element using jQuery datatable to indicate that the update operation was

I have implemented inline editing using jQuery Datatables. Currently, I am trying to display a green checkmark when a record gets updated. Below is the ajax call that populates the table: $.ajax({ url: 'api/massEditorSummary.php', type: &ap ...