0

I have an Array with multiple Objects, now I want to loop each object as a <tr> in a <table>. I have done that.

However, these objects may contain multiple objects. Then I want to display all objects within 1 object in 1 <td>.

So this is the code what I have currently:

<tr v-for="(data, index) in tableData" :key="index">
  <td
    v-for="(propertyValue, property, propertyIndex) in data.tableRow"
    :key="property"
  >
    <div>
      <span>{{ propertyValue }}</span>
    </div>
  </td>
</tr>
const tableData = [
  {
    tableRow: {
      name: 'test',
    },
    tableRow1: {
      test: 'testComponent',
    },
  },
  {
    tableRow: {
      name: 'test 1',
    },
  },
  {
    tableRow: {
      name: 'test 2',
    },
  },
];

This should also happen for the other td's when there are multiple properties in the 2nd object.

How can I do this?

1 Answer 1

1

Option 1: Calculate rowComponent per iteration

This can be done with a function that calculates the rowComponent for a particular column:

const getRowComponent = ({ rowComponent }, index) =>
  // Only proceed if rowComponent exists
  rowComponent &&

  // Get the object values of rowComponent as an array
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Object/values
  Object.values(rowComponent)

  // Get array element at specified index (undefined if not found)
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at
  .at(index)

The function would be used in the template per item of tableData, using the rowData index (named propertyIndex in this case):

<tr v-for="(data, index) in tableData" :key="index">
  <td v-for="(propertyValue, property, propertyIndex) in data.rowData" :key="property" class="table-body-data">
    <div class="table-body-data-item">
      <span>{{ propertyValue }}</span>
                      👇
      <span>{{ getRowComponent(data, propertyIndex) }}</span>
    </div>
  </td>
</tr>

demo 1

However, if performance is to be considered (e.g., there are many rows and columns), this solution would be inefficient because:

  • The function is called whenever this component is rendered, which can be multiple times.
  • The function calls Object.values() on the same object for each column, thus repeating work that was done on the previous column.

If there are only a few columns, this hit probably wouldn't matter much.

Option 2: Compute collated data, and render that

A more performant solution is to use a computed property that collates the corresponding rowData and rowComponent, returning an array that better facilitates accessing the paired data than the tableData object:

// https://stackoverflow.com/a/22015930/6277151
const zip = (a, b) => Array.from(Array(Math.max(b.length, a.length)), (_, i) => [a[i], b[i]])
const zipValues = (a, b) => zip(Object.values(a ?? {}), Object.values(b ?? {}))

// Computed table is an array of rows.
// Each row is an array of columns.
// Each column is an array of cell text.
const computedTableData = computed(() => tableData.map(item => zipValues(item.rowData, item.rowComponent)))

Then use the computed array in the template instead of tableData:

<tr v-for="(row, rowIndex) in computedTableData" :key="rowIndex">
  <td v-for="(col, colIndex) in row" :key="colIndex" class="table-body-data">
    <div class="table-body-data-item">
      <span v-for="(cellText, cellTextIndex) in col" :key="cellTextIndex">{{ cellText }}</span>
    </div>
  </td>
</tr>

demo 2

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

Comments

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.