Home > Net >  Table header data from JSON in Svelte table
Table header data from JSON in Svelte table

Time:05-26

I'm messing around with Svelte and the sb-admin-svelte project. I'm working with some sample data from an API in JSON and I have it working so far, but my question is how do I get the key items from the JSON data to automatically parse as a table header in the HTML?

This is what I have so far:

JSON

[
  {
    "id": 1,
    "name": "Leanne Graham",
    "username": "Bret",
    "email": "[email protected]",
    "address": {
      "street": "Kulas Light",
      "suite": "Apt. 556",
      "city": "Gwenborough",
      "zipcode": "92998-3874",
      "geo": {
        "lat": "-37.3159",
        "lng": "81.1496"
      }
    },
    "phone": "1-770-736-8031 x56442",
    "website": "hildegard.org",
    "company": {
      "name": "Romaguera-Crona",
      "catchPhrase": "Multi-layered client-server neural-net",
      "bs": "harness real-time e-markets"
    }
  },
  {
    "id": 2,
    "name": "Ervin Howell",
    "username": "Antonette",
    "email": "[email protected]",
    "address": {
      "street": "Victor Plains",
      "suite": "Suite 879",
      "city": "Wisokyburgh",
      "zipcode": "90566-7771",
      "geo": {
        "lat": "-43.9509",
        "lng": "-34.4618"
      }
    },
    "phone": "010-692-6593 x09125",
    "website": "anastasia.net",
    "company": {
      "name": "Deckow-Crist",
      "catchPhrase": "Proactive didactic contingency",
      "bs": "synergize scalable supply-chains"
    }
  },
  {
    "id": 3,
    "name": "Clementine Bauch",
    "username": "Samantha",
    "email": "[email protected]",
    "address": {
      "street": "Douglas Extension",
      "suite": "Suite 847",
      "city": "McKenziehaven",
      "zipcode": "59590-4157",
      "geo": {
        "lat": "-68.6102",
        "lng": "-47.0653"
      }
    },
    "phone": "1-463-123-4447",
    "website": "ramiro.info",
    "company": {
      "name": "Romaguera-Jacobson",
      "catchPhrase": "Face to face bifurcated interface",
      "bs": "e-enable strategic applications"
    }
  }
...

JS

<script>
  import Table from "sveltestrap/src/Table.svelte";
  import { onMount } from "svelte";

  let users = [];

  onMount(async () => {
    const res = await fetch(`https://jsonplaceholder.typicode.com/users`);
    users = await res.json();

    console.log(users);
  });

  const tableHeading = ["ID", "Name", "Username", "email"];
  
</script>

<Table bordered responsive>
  <thead>
    <tr>
      {#each tableHeading as heading}
        <th>{heading}</th>
      {/each}
    </tr>
  </thead>
  <tbody>

    {#each users as user}
      <tr>
        <th scope="row">{user.id}</th>
        <td>{user.name}</td>
        <td>{user.username}</td>
        <td>{user.email}</td>
      </tr>
    {/each}
  </tbody>
</Table>

Which outputs to this:

svelte table

Which is fine but what I am trying to figure out is how to get the tableHeading data from the JSON data (e.g. "id", "name" "username", "email) to automatically populate within the {#each tableHeading as heading}.

CodePudding user response:

Is this what you want to do? REPL

<script>
    import Table from "sveltestrap/src/Table.svelte";
    import { onMount } from "svelte";

    async function fetchUsers() {
        const res = await fetch(`https://jsonplaceholder.typicode.com/users`);
        return await res.json();
    }   
</script>

{#await fetchUsers() then users}
<Table bordered responsive>
    <thead>
        <tr>
            {#each Object.keys(users[0]).slice(0,4) as heading}
            <th>{heading}</th>
            {/each}
        </tr>
    </thead>
    <tbody>

        {#each users as user}
        <tr>
            <th scope="row">{user.id}</th>
            <td>{user.name}</td>
            <td>{user.username}</td>
            <td>{user.email}</td>
        </tr>
        {/each}
    </tbody>
</Table>
{/await}

But why not automatically populate the table content as well, based on a given number of columns REPL

<script>
    import Table from "sveltestrap/src/Table.svelte";
    import { onMount } from "svelte";

    async function fetchUsers() {
        const res = await fetch(`https://jsonplaceholder.typicode.com/users`);
        return await res.json();
    }

    const columns = 4

</script>

{#await fetchUsers() then users}
<Table bordered responsive>
    <thead>
        <tr>
            {#each Object.keys(users[0]).slice(0,columns) as heading}
            <th>{heading}</th>
            {/each}
        </tr>
    </thead>
    <tbody>
        {#each users as user}
        <tr>
            {#each Object.values(user).slice(0,columns) as value, index}
                {#if index===0}
                    <th scope="row">{value}</th>
                {:else}
                    <td>{value}</td>
                {/if}
            {/each}
        </tr>
        {/each}
    </tbody>
</Table>
{/await}

For the inner #each loop inside the table body the new svelte:element could be used instead (but not really the better option in this case)

{#each Object.values(user).slice(0,columns 1) as value, index}
    <svelte:element this={index === 0 ? 'th': 'td'}
                    scope={index === 0 ? 'row' : undefined}
                    >
        {value}
    </svelte:element>
{/each}

But a more flexible and convenient way would be to populate everything, like you already did, by a defined tableHeading and fill the table body based on that. Like this unwanted values can simply be omitted and the order easily changed REPL

<script>
    import Table from "sveltestrap/src/Table.svelte";
    import { onMount } from "svelte";

    async function fetchUsers() {
        const res = await fetch(`https://jsonplaceholder.typicode.com/users`);
        return await res.json();
    }

    const tableHeading = ["ID", "Name", "Website", "email", "Username", "Phone"];

</script>

{#await fetchUsers() then users}
<Table bordered responsive>
    <thead>
        <tr>
            {#each tableHeading as heading}
            <th>{heading}</th>
            {/each}
        </tr>
    </thead>
    <tbody>
        {#each users as user}
        <tr>
            {#each tableHeading as key, index}
                {@const k = key.toLowerCase()}
                {#if index===0}
                    <th scope="row">{user[k]}</th>
                {:else}
                    <td>{user[k]}</td>
                {/if}
            {/each}
        </tr>
        {/each}
    </tbody>
</Table>
{/await}
  • Related