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