Search code examples
vue.jsvue-componentvuejs3vue-composition-api

Rendering multiple custom dynamic tables in Vuejs


A couple of months ago I asked a question on how to render a custom dynamic table based on a given JSON object returned from an API. Here is the link for the question I asked before: Rendering a custom table in Vuejs

And the solution that I employed is effectively what has been posted as a response to my question. What I'm looking for now is to render multiple tables on the same page in a similar fashion.

The json object would look like this:

[
   {
      "id": 1, 
      "enabled": true,
      "headers": [
          {
            "id": 1, 
            "header_name": "LastName",
            "colVal": [{ "id": 1, "value": "Hopper" }
                      { "id": 3, "value": "Lovelace"}]
          }, 
          {
            "id": 2, 
            "header_name": "FirstName",
            "colVal": [{ "id": 1, "value": "Grace" }
                      { "id": 4, "value": "Ada" }]
          }
       ]
   }, 

   {
      "id": 2, 
      "enabled": true,
      "headers": [
          {
            "id": 1, 
            "header_name": "Age",
            "colVal": [{ "id": 1, "value": 22 }
                      { "id": 3, "value": 31}]
          }, 
          {
            "id": 2, 
            "header_name": "Phone Number",
            "colVal": [{ "id": 1, "value": 123-456 }
                      { "id": 4, "value": 789-543 }]
          }
       ]
   }
]

The two tables that I want to get out of this json object are:

LastName FirstName
Hopper Grace
Lovelace Ada

And

Age Phone Number
22 123-456
31 789-543

Solution

  • Create a component for one table based on your solution before and and then iterate over your array with v-for.

    <div v-for="table in tables">
        <my-table :data="table" />
    </div>
    

    Here is it

    const { createApp, ref, computed } = Vue;
    
    const tables = 
    [
      {
        "id": 1,
        "enabled": true,
        "headers": [
          {
            "id": 1,
            "header_name": "LastName",
            "colVal": [ { "id": 1, "value": "Hopper" } ]
          },
          {
            "id": 2,
            "header_name": "FirstName",
            "colVal": [ { "id": 1, "value": "Grace" } ]
          }
        ]
      },
      {
      "id": 2,
      "enabled": true,
      "headers": [
        {
          "id": 1,
          "header_name": "LastName",
          "colVal": [
            { "id": 1, "value": "Hopper" },
            { "id": 3, "value": "Lovelace" }
          ]
        },
        {
          "id": 2,
          "header_name": "FirstName",
          "colVal": [
            { "id": 2, "value": "Grace" },
            { "id": 4, "value": "Ada" }
          ]
        }
      ]
    }
    ]
    
    const MyTable = {
      props: ['data'],
      setup(props) {
        const headers = computed(()=>{
          return props.data?.headers.map(h => ({header:h.header_name, id:h.id}))
        })
    
        const rows = computed(()=>{
          let rowData = [];
          props.data?.headers.forEach(h => {
            let key = h.header_name
            h.colVal.forEach((r, i) => {
              if(!rowData[i]) {rowData[i] = {id:i}}
              rowData[i][key] = r.value
            })
          })
          return rowData
        })
        return { headers, rows } 
      },
      template: '#my-table-template'
    }
    
    const App = { 
      components: { MyTable },
      setup(props) {
        return { tables } 
      }
    }
    const app = createApp(App)
    app.mount('#app')
    #app { line-height: 2; }
    [v-cloak] { display: none; }
    table, th, td { border: 1px solid lightgrey; }
    th { background-color: lightgrey; }
    th, td { text-align: center; width: 1%;}
    <div id="app" v-cloak>
    <div v-for="table in tables">
    <my-table :data="table"></my-table><br/>
    </div>
    </div>
    <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
    <script type="text/x-template" id="my-table-template">
    <table v-if="headers">
      <thead>
        <tr>
          <th v-for="{header, id} in headers" :key="id">{{header}}</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="row in rows" :key="row.id">
            <td v-for="{header, id} in headers">
              {{ row[header] }}
            </td>
        </tr>
      </tbody>
    </table>
    </script>