Tips for maximizing efficiency in binding data to a table that presents information in column format

I'm struggling to figure out how to effectively utilize computed properties when presenting data in columns within a table format. You can view the complete code example at jsfiddle, here's a condensed version with an explanation. My goal is to display this data in a table:

var vueData = {
  objects: [
    {
      name: "objectName",
      objectData: [
        {prop1: "val1", prop2: "val2"}
      ]
    }
  ]
}

In this vueData, each element of the objectData array should be shown as a column (representing data for one month or day). The properties in the elements of objectData should not be displayed as they are, but as calculated values. And these displayed values should update based on changes in vueData.

So, I crafted this vue template:

<table>
<tr>
  <th>Object Name</th>
  <th>Data Piece 1</th>
  <th>Data Piece 2</th>
</tr>
<template v-for="obj in objects">
<tr> 
  <th rowspan="2">{{ obj.name }}</th>
  <td v-for="dataPiece in obj.objectData">{{ compute(dataPiece.prop1) }}</td>
</tr>
<tr><td v-for="dataPiece in obj.objectData">{{ compute(dataPiece.prop2) }}</td></tr>
</template>
</table>

Everything functions correctly except that I used methods instead of Vue's computed properties. The issue with methods is that their results aren't cached, so after changing just one property, ALL cell values are recalculated.

I could opt for a Vue component for each cell using a computed property instead of using tds, but it seems like overkill since the table could be large.

Is there another solution from a Vue perspective? (Without altering the data structure or appearance of the table, etc).

Answer №1

I initially wanted to implement this functionality with a directive, but I struggled with passing both the function and argument effectively. That's when it dawned on me that I could utilize vnode.context to access the context and retrieve the function by name rather than directly passing it as a function.

So, here's a custom directive that dynamically updates the textContent of its element based on the result of a function call.

// Vue data object
var vueData = {
  objects: [{
      name: 'Object 1',
      objectData: [{
          prop1: '1-1-1',
          prop2: '1-1-2'
        },
        {
          prop1: '1-2-1',
          prop2: '1-2-2'
        },
        {
          prop1: '1-3-1',
          prop2: '1-3-2'
        }
      ]
    },
    // More object entries...
  ]
};

// Vue instance creation
var vue = new Vue({
  el: document.getElementById("vue"),
  data: vueData,
  methods: {
    compute: function(prop) {
      console.log('computing ' + prop);
      return 'computed(' + prop + ')';
    }
  },
  directives: {
    cache(el, binding, vnode) {
      if (binding.value !== binding.oldValue) {
        el.textContent = vnode.context[binding.arg](binding.value);
      }
    }
  },
  computed: {
    firstProp: function() {
      return this.objects[0].objectData[0].prop1;
    }
  }
});

// Simulating data change after a delay
setTimeout(function() {
  vueData.objects[0].objectData[0].prop1 = 'changed on timeout';
}, 3000);
th,
td {
  border: 1px solid black;
}
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<div id="vue">
  <table>
    <tr>
      <th>Object Name</th>
      <th>Data Piece 1</th>
      <th>Data Piece 2</th>
      <th>Data Piece 3</th>
    </tr>
    <template v-for="obj in objects">
      <tr>
        <th rowspan="2">{{ obj.name }}</th>
        <td v-for="dataPiece in obj.objectData"
          v-cache:compute="dataPiece.prop1"
          >
        </td>
      </tr>
      <tr>
        <td v-for="dataPiece in obj.objectData"
          v-cache:compute="dataPiece.prop2">
        </td>
      </tr>
    </template>
  </table>
  <span>Computed prop1 value = {{ firstProp }}</span>
</div>

Answer №2

While browsing, I stumbled upon a post from LinusBorg titled Generating computed properties on the fly, where he presents a function for mapping properties to computed values.

I made some adjustments to the function specifically for your 'objects' variable since the original code was more tailored for flat form data and seemed a bit complex.

function mapObjectToComputed({objects}) {
  console.log(objects)
  let res = {};
  objects.forEach((obj,i) => {
    obj.objectData.forEach((dat,j) => {
      ['prop1', 'prop2'].forEach(prop => {
        const propModel = `objects_${i}_${j}_${prop}`;
        const computedProp = {
          get() {
            console.log(`Getting ${propModel}`)
            const val = this.objects[i].objectData[j][prop];
            return val;
          }
        }
        res[propModel] = computedProp;
      })
    })
  })
  return res;
}

In the following template snippet, I've updated the syntax for 'prop1' and left 'prop2' untouched as per the original implementation.

<template v-for="(obj, i) in objects">
  <tr> 
    <th rowspan="2">{{ obj.name }}</th>
    <td v-for="(dataPiece, j) in obj.objectData">
      {{ fetch(i,j,'prop1') }}
    </td>
  </tr>
  <tr><td v-for="dataPiece in obj.objectData">
    {{ compute(dataPiece.prop2) }}
  </td></tr>
</template>

The component declarations are as follows:

var vue = new Vue({
    el: document.getElementById("vue"),
  data: vueData,
  methods: {
    fetch: function(i,j,prop) {
      const propModel = `objects_${i}_${j}_${prop}`
      return this[propModel];
    },
    compute: function(prop) {
        console.log('computing ' + prop);
        return 'computed(' + prop + ')';
    }
  },
  computed: {
    firstProp: function() {
        return this.objects[0].objectData[0].prop1;
    },
    ...mapObjectToComputed(vueData)
  }
});

The console output after a timeout period displays:

Getting objects_0_0_prop1  
computing 1-1-2  
computing 1-2-2  
computing 1-3-2  
computing 2-1-2  
computing 2-2-2  
computing 2-3-2  
computing 3-1-2  
computing 3-2-2  
computing 3-3-2  

Resulting in only 'prop2' recalculating across all instances.

You can view the demo on Fiddle

Answer №3

Using a component is the recommended solution as it provides a clean approach to solving the problem at hand. The example shown here demonstrates a simple yet effective implementation that can be valuable, especially in scenarios where computations are resource-intensive.

var vueData = {
  objects: [{
      name: 'Object 1',
      objectData: [{
          prop1: '1-1-1',
          prop2: '1-1-2'
        },
        {
          prop1: '1-2-1',
          prop2: '1-2-2'
        },
        {
          prop1: '1-3-1',
          prop2: '1-3-2'
        }
      ]
    },
    {
      name: 'Object 2',
      objectData: [{
          prop1: '2-1-1',
          prop2: '2-1-2'
        },
        {
          prop1: '2-2-1',
          prop2: '2-2-2'
        },
        {
          prop1: '2-3-1',
          prop2: '2-3-2'
        }
      ]
    },
    {
      name: 'Object 3',
      objectData: [{
          prop1: '3-1-1',
          prop2: '3-1-2'
        },
        {
          prop1: '3-2-1',
          prop2: '3-2-2'
        },
        {
          prop1: '3-3-1',
          prop2: '3-3-2'
        }
      ]
    },
  ]
};

var vue = new Vue({
  el: document.getElementById("vue"),
  data: vueData,
  methods: {
    compute: function(prop) {
      console.log('computing ' + prop);
      return 'computed(' + prop + ')';
    }
  },
  components: {
    cacheResult: {
      props: {
        fn: Function,
        arg: String
      },
      template: '<td>{{fn(arg)}}</td>'
    }
  },
  computed: {
    firstProp: function() {
      return this.objects[0].objectData[0].prop1;
    }
  }
});

setTimeout(function() {
  vueData.objects[0].objectData[0].prop1 = 'changed on timeout';
}, 3000);
th,
td {
  border: 1px solid black;
}
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<div id="vue">
  <table>
    <tr>
      <th>Object Name</th>
      <th>Data Piece 1</th>
      <th>Data Piece 2</th>
      <th>Data Piece 3</th>
    </tr>
    <template v-for="obj in objects">
      <tr>
        <th rowspan="2">{{ obj.name }}</th>
        <td v-for="dataPiece in obj.objectData"
          is="cacheResult"
          :fn="compute"
          :arg="dataPiece.prop1"
          >
        </td>
      </tr>
      <tr>
        <td v-for="dataPiece in obj.objectData"
          is="cacheResult"
          :fn="compute"
          :arg="dataPiece.prop2">
        </td>
      </tr>
    </template>
  </table>
  <span>Computed prop1 value = {{ firstProp }}</span>
</div>

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

Why bother using useCallback in a component if React.memo is not utilized alongside it?

Here's an example to consider: const Child = () => { console.log("I did re-render!"); return null; }; const App = () => { const [_, setValue] = useState(); const fn = useCallback(() => { // do something }, []); ret ...

I'm trying to figure out how to save a Mongoose query and then pass it as a parameter to render an EJS page. How can I achieve this

I'm currently in the process of constructing an admin dashboard, and one feature I want to include is displaying mongoose data such as user information and recent tutoring sessions. However, I'm facing challenges when it comes to saving this data ...

Simple method to toggle between elements using jQuery

After creating a script using jQuery to show/hide content when tabs are clicked (which also changes the color of the clicked tab), everything is functioning smoothly. However, I believe there may be a more efficient way to switch between content that allow ...

Nested scrolling bars within each other

Currently, I am importing photos from a Facebook page and displaying them on my jQuery mobile webpage using the photoSwipe plugin. However, there seems to be an issue with the final appearance of the images. Pay attention to the image where the red arrow ...

Guide to modifying Button on fetch response in React Native

After receiving a response from the server, I need to adjust the button based on the friends_status field in the following response: { "responseHeader": { "type": "314", "status": "200", "message": "Successfully found the profile" }, "ne ...

In JavaScript, split the array containing both first and last names into separate variables for first and last name

Looking for a solution to split an array element in a web template, where the size of the array varies per page. The array is in the format: ['John gray','Matt jones', 'Frank white']. The goal is to separate this array into tw ...

Having trouble accessing an injector service within the promise of a dynamically loaded JavaScript function that has been assigned to a global variable

Query I am facing an issue while trying to integrate PayPal with Angular. I am encountering difficulties when attempting to call an injected service inside a function of the promise returned. Any assistance in resolving this would be greatly appreciated. ...

Issues with grunt - Alert: Task "ngAnnotate:dist" has encountered an error. Proceed using --force option

Encountering an unexpected issue with a Grunt task that previously ran smoothly. The error message is as follows: Running "ngAnnotate:dist" (ngAnnotate) task Generating ".tmp/concat/scripts/scripts.js" from: ".tmp/concat/scripts/scripts.js"...ERROR >& ...

Is it possible to use Symbol.iterator in an Object via prototype in JavaScript?

What is the reason behind the inability to add Symbol.iterator to an Object in this way? Object.prototype[Symbol.iterator]; let obj = {num: 1 , type : ' :) '} for (let p of obj) { console.log(p); } // TypeError: obj is no ...

Mocha avoids running the test

Hi everyone, I am a beginner in Node.js and JavaScript and I'm having trouble understanding why Mocha is skipping over my test. I know that I may not be using the request and supertest libraries correctly, but I really want to figure out why, when it ...

Exploring the Ins and Outs of Transition Testing in D3 v4

Previously, in older versions of D3, you were able to create unit tests that checked the state of a D3 component after all transitions had finished by flushing the timer with d3.timer.flush(). However, in D3 version 4, this has changed to d3.timerFlush(), ...

"Error alert: The specified property 'Iscontains' cannot be read because it is undefined" appears in the script for this video

I am currently utilizing AJAX to interact with my backend code. I retrieve URLs from the database and then render them onto the DOM, displaying videos accordingly. However, I am encountering an issue where Uncaught TypeError: Cannot read property ' ...

Utilizing jQuery to iterate over dynamically generated elements sharing a common class

Whenever I click a button, numerous div elements are dynamically created. <table> <tbody id="ProductDetail"></tbody> </table> These dynamically created divs have an associated Amount value that is added upon creation. funtion ...

tag of data in jquery

Here is how my setup looks: <div onclick="EditCalendarEvent('@_schedulerEvent.SchedulerID','@_schedulerEvent.SchedulerItemID', event)" class="ScheduleEvent_Draggable ScheduleEvent" data-schedulerID="@_schedul ...

Include personalized headers to the 'request'

I have configured my express server to proxy my API using the following setup: // Proxy api calls app.use('/api', function (req, res) { let url = config.API_HOST + req.url req.pipe(request(url)).pipe(res) }) In this instance, confi ...

What methods can I use to create a peer-to-peer communications platform with PHP, MySQL database, and JavaScript?

Currently, I am encountering the challenge of creating a communication channel between two users on a website (such as a gaming site) using only the technologies specified in the title. I recently created an online chess platform with the aim of allowing ...

Comparing two datetime objects with time zone offsets in JavaScript: How to determine if one is greater than or less than the other?

So I'm faced with a situation where I need to compare two dates where the first date is 05.01.2008 6:00 +5:00 and the second date is 05.01.2008 7:00 +5:00 I'm struggling to find a way to convert these datetimeoffsets into a specific forma ...

Vuex getter's forEach implementation fetches a singular value rather than multiple values

I am currently working on a project with a shopping cart that stores products in an array. I am facing an issue where I am trying to access every product name in the cart. Although I have tried using the forEach function in getters, it only returns a sin ...

A simple way to verify which option has been chosen has been altered

I am examining the code snippet below: <select class="select" id="choice-item" aria-invalid="false"> <option value="#opt0" selected="selected">32 bits</option> <option value="#opt1">64 bits</option> </select> M ...

Injecting Vue components and dynamically rendering them in real-time

Presently, I am in the process of developing a third-party application where Vue.js is being utilized to render components. In this setup, the third-party app loads the component dynamically after the Vue app has been initialized and mounted. Due to this ...