javascript – json cannot be rendered as table in vue.js app

I am able to render json in my vue app from an endpoint ‘/v1/api/sse?raceId=1’ but I cannot figure out how to make the json into a table. The json is rendering fine for me however I do not know how to take this json and form it into a table.

Here’s what the json payload looks like:

{
  "raceId": 1,
  "driverId": "17",
  "lap": 2,
  "position": 19,
  "time": "01:49.3",
  "milliseconds": 109274,
  "driverRef": "webber",
  "forename": "Mark",
  "surname": "Webber",
  "dob": "27/08/1976",
  "nationality": "Australian",
  "url": "http://en.wikipedia.org/wiki/Mark_Webber"
}

Here’s my app.vue file:

  <div id="app">
    <div>
      <div>
        <p>
          <label>URL:</label>
          <input type="text" v-model="url" placeholder="http://">
        </p>

        <p>
          <label>Format:</label>
          <select v-model="format">
            <option value="plain">Plain</option>
            <option value="json">JSON</option>
          </select>
        </p>

        <p>
          <label>
            <input type="checkbox" v-model="includeCredentials">
            Include credentials?
          </label>
        </p>

        <p>
          <button :disabled="url.length === 0" @click.prevent="connect">Connect</button>
          <button @click.prevent="disconnect">Disconnect</button>
          <button @click.prevent="clear">Clear</button>
        </p>
      </div>

      <handlers
        :handlers.sync="handlers"
      />
    </div>

    <div>
      <LogDisplay
        :logs="logs"
      />
    </div>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'
import { SSEClient } from 'vue-sse/types'
import Handlers from './components/Handlers.vue'
import LogDisplay from './components/LogDisplay.vue'
import { now } from './utils'

let client: SSEClient | null

export default Vue.extend({
  name: 'App',
  components: {
    Handlers,
    LogDisplay
  },
  data () {
    return {
      url: '/v1/api/sse?raceId=1',
      generateColumns: 'test',
      includeCredentials: false,
      format: 'json',
      handlers: [
        {
          event: 'message',
          color: '#60778e'
        }
      ],
      logs: [] as [string, string, string][]
    }
  },
  methods: {
    connect () {
      // try disconnecting, just in case
      this.disconnect()

      this.log(`[info] connecting to ${this.url}`, 'system')

      // create the client with the user's config
      client = this.$sse.create({
        url: this.url,
        includeCredentials: this.includeCredentials,
        format: this.format
      })

      // add the user's handlers
      this.handlers.forEach((h) => {
        client!.on(h.event, (data) => { // eslint-disable-line
          this.log(data, h.color)
        })
      })

      client!.on('error', () => { // eslint-disable-line
        this.log('[error] disconnected, automatically re-attempting connection', 'system')
      })

      // and finally -- try to connect!
      client!.connect() // eslint-disable-line
        .then(() => {
          this.log('[info] connected', 'system')
        })
        .catch(() => {
          this.log('[error] failed to connect', 'system')
        })
    },

    disconnect () {
      if (client) {
        client.disconnect()
        client = null
        this.log('[info] disconnected', 'system')
      }
    },

    clear () {
      this.logs = []
    },

    log (message: string, color: string) {
      this.logs.push([now(), message, color])
    }
  },
  beforeDestroy () {
    this.disconnect()
  }
})
</script>

<style>
html, body {
  margin: 0;
  padding: 0;
}

h2 {
  display: inline;
}

button, input, select {
  font: inherit;
}

#app {
  background: #dddddd;
  font-family: sans-serif;
  min-height: 100vh;
  position: relative;
}

@media(min-width: 768px) {
  #app {
    display: flex;
  }

  #app > div {
    padding: 1rem;
  }

  #app > div:first-child {
    text-align: left;
  }

  #app > div:last-child {
    flex: 1;
  }
}
</style>

And here’s my index.html:

<html lang="en">

<head>

  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta name="description" content="">
  <meta name="author" content="">

  <title>Realtime Betting - Formula 1 Racing</title>

  <!-- Custom fonts for this template -->
  <link href="vendor/fontawesome-free/css/all.min.css" rel="stylesheet" type="text/css">
  <link href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i" rel="stylesheet">

  <!-- Custom styles for this template -->
  <link href="css/sb-admin-2.min.css" rel="stylesheet">

  <!-- Custom styles for this page -->
  <link href="vendor/datatables/dataTables.bootstrap4.min.css" rel="stylesheet">

  <!-- PNotify -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/pnotify/2.0.0/pnotify.core.min.css">

</head>
<body id="page-top">

  <!-- Page Wrapper -->
  <div id="wrapper">

    <!-- Sidebar -->
    <ul class="navbar-nav bg-danger sidebar sidebar-dark accordion" id="accordionSidebar">

      <!-- Sidebar - Brand -->
      <a class="sidebar-brand d-flex align-items-center justify-content-center" href="">
        <div class="sidebar-brand-icon rotate-n-15">
          <i class="fa fa-flag-checkered"></i>
          <!-- <img class="img-fluid px-3 px-sm-4 mt-3 mb-4" style="width: 25rem;" src="https://upload.wikimedia.org/wikipedia/commons/thumb/3/33/F1.svg/1280px-F1.svg.png" alt=""> -->
        </div>
        <div class="sidebar-brand-text mx-3">Formula 1</div>
      </a>

      <!-- Divider -->
      <hr class="sidebar-divider my-0">

      <!-- Nav Item - Dashboard -->
      <!-- <li class="nav-item">
        <a class="nav-link" href="/">
          <i class="fas fa-fw fa-tachometer-alt"></i>
          <span>Dashboard</span></a>
      </li> -->

      <!-- Divider -->
      <!-- <hr class="sidebar-divider"> -->

      <br />

      <!-- Heading -->
      <div class="sidebar-heading">
        Data
      </div>

      <!-- Nav Item - Charts -->
      <li v-for="endpoint in this.endpoints" class="nav-item">
        <!-- <a class="nav-link" href="charts.html"> -->
        <a class="nav-link" :href="endpoint.name.toLowerCase()">
          <i class="fas fa-fw fa-chart-area"></i>
          <span>{{ endpoint.name }}</span></a>
      </li>


    </ul>
    <!-- End of Sidebar -->

    <!-- Content Wrapper -->
    <div id="content-wrapper" class="d-flex flex-column">

      <!-- Main Content -->
      <div id="content">

        <!-- Topbar -->
        <nav class="navbar navbar-expand navbar-light bg-white topbar mb-4 static-top shadow">

          <!-- Sidebar Toggle (Topbar) -->
          <button id="sidebarToggleTop" class="btn btn-link d-md-none rounded-circle mr-3">
            <i class="fa fa-bars"></i>
          </button>

          <!-- Topbar Search -->
          <form class="d-none d-sm-inline-block form-inline mr-auto ml-md-3 my-2 my-md-0 mw-100 navbar-search">
            <div class="input-group">
              <input type="text" class="form-control bg-light border-0 small" placeholder="Search for..." aria-label="Search" aria-describedby="basic-addon2">
              <div class="input-group-append">
                <button class="btn btn-danger" type="button">
                  <i class="fas fa-search fa-sm"></i>
                </button>
              </div>
            </div>
          </form>

          <!-- Topbar Navbar -->
          <ul class="navbar-nav ml-auto">

            <!-- Nav Item - Search Dropdown (Visible Only XS) -->
            <li class="nav-item dropdown no-arrow d-sm-none">
              <a class="nav-link dropdown-toggle" href="#" id="searchDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                <i class="fas fa-search fa-fw"></i>
              </a>
              <!-- Dropdown - Messages -->
              <div class="dropdown-menu dropdown-menu-right p-3 shadow animated--grow-in" aria-labelledby="searchDropdown">
                <form class="form-inline mr-auto w-100 navbar-search">
                  <div class="input-group">
                    <input type="text" class="form-control bg-light border-0 small" placeholder="Search for..." aria-label="Search" aria-describedby="basic-addon2">
                    <div class="input-group-append">
                      <button class="btn btn-primary" type="button">
                        <i class="fas fa-search fa-sm"></i>
                      </button>
                    </div>
                  </div>
                </form>
              </div>
            </li>

            <!-- Nav Item - Alerts -->
            <li class="nav-item dropdown no-arrow mx-1">
              <a class="nav-link dropdown-toggle" href="#" id="alertsDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                <i class="fas fa-bell fa-fw"></i>
                <!-- Counter - Alerts -->
                <span class="badge badge-danger badge-counter">3+</span>
              </a>
              <!-- Dropdown - Alerts -->
              <div class="dropdown-list dropdown-menu dropdown-menu-right shadow animated--grow-in" aria-labelledby="alertsDropdown">
                <h6 class="dropdown-header">
                  Alerts Center
                </h6>
                <a class="dropdown-item d-flex align-items-center" href="#">
                  <div class="mr-3">
                    <div class="icon-circle bg-primary">
                      <i class="fas fa-file-alt text-white"></i>
                    </div>
                  </div>
                  <div>
                    <div class="small text-gray-500">December 12, 2019</div>
                    <span class="font-weight-bold">A new monthly report is ready to download!</span>
                  </div>
                </a>
                <a class="dropdown-item d-flex align-items-center" href="#">
                  <div class="mr-3">
                    <div class="icon-circle bg-success">
                      <i class="fas fa-donate text-white"></i>
                    </div>
                  </div>
                  <div>
                    <div class="small text-gray-500">December 7, 2019</div>
                    $290.29 has been deposited into your account!
                  </div>
                </a>
                <a class="dropdown-item d-flex align-items-center" href="#">
                  <div class="mr-3">
                    <div class="icon-circle bg-warning">
                      <i class="fas fa-exclamation-triangle text-white"></i>
                    </div>
                  </div>
                  <div>
                    <div class="small text-gray-500">December 2, 2019</div>
                    Spending Alert: We've noticed unusually high spending for your account.
                  </div>
                </a>
                <a class="dropdown-item text-center small text-gray-500" href="#">Show All Alerts</a>
              </div>
            </li>


              <!-- Dropdown - User Information -->
              <div class="dropdown-menu dropdown-menu-right shadow animated--grow-in" aria-labelledby="userDropdown">
                <a class="dropdown-item" href="#">
                  <i class="fas fa-user fa-sm fa-fw mr-2 text-gray-400"></i>
                  Profile
                </a>
                <a class="dropdown-item" href="#">
                  <i class="fas fa-cogs fa-sm fa-fw mr-2 text-gray-400"></i>
                  Settings
                </a>
                <a class="dropdown-item" href="#">
                  <i class="fas fa-list fa-sm fa-fw mr-2 text-gray-400"></i>
                  Activity Log
                </a>
                <div class="dropdown-divider"></div>
                <a class="dropdown-item" href="#" data-toggle="modal" data-target="#logoutModal">
                  <i class="fas fa-sign-out-alt fa-sm fa-fw mr-2 text-gray-400"></i>
                  Logout
                </a>
              </div>
            </li>

          </ul>

        </nav>
        <!-- End of Topbar -->

        <!-- Begin Page Content -->
        <div class="container-fluid">

          <!-- Page Heading -->
          <h1 class="h8 mb-2 text-gray-800">Realtime Betting - Raw Racecar Feed</h1>
          <p class="mb-2">Code adapted from original repo here: <a target="_blank" href="https://github.com/Darrellrp/Formula-1-App">GitHub</a></p>

          <!-- DataTales Example -->
          <div id="table" class="card shadow mb-4">
            <div class="card-header py-3">
              <h6 class="m-0 font-weight-bold text-danger">RACERS</h6>
            </div>
            <div class="card-body">
              <div class="table-responsive">
                <div id="app"></div>
                <table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">

                </table>
              </div>
            </div>
          </div>

        </div>
        <!-- /.container-fluid -->

      </div>
      <!-- End of Main Content -->

      <!-- Footer -->
      <footer class="sticky-footer bg-white">
        <div class="container my-auto">
          <div class="copyright text-center my-auto">
            <span><a target="_blank" href="https://github.com/Darrellrp/Formula-1-App">Darrellrp/Formula-1-App</a></span>
          </div>
        </div>
      </footer>
      <!-- End of Footer -->

    </div>
    <!-- End of Content Wrapper -->

  </div>
  <!-- End of Page Wrapper -->

  <!-- Scroll to Top Button-->
  <a class="scroll-to-top rounded" href="#page-top">
    <i class="fas fa-angle-up"></i>
  </a>

  <!-- Logout Modal-->
  <div class="modal fade" id="logoutModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
    <div class="modal-dialog" role="document">
      <div class="modal-content">
        <div class="modal-header">
          <h5 class="modal-title" id="exampleModalLabel">Ready to Leave?</h5>
          <button class="close" type="button" data-dismiss="modal" aria-label="Close">
            <span aria-hidden="true">×</span>
          </button>
        </div>
        <div class="modal-body">Select "Logout" below if you are ready to end your current session.</div>
        <div class="modal-footer">
          <button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
          <a class="btn btn-primary" href="login.html">Logout</a>
        </div>
      </div>
    </div>
  </div>

  <!-- Bootstrap core JavaScript-->
  <script src="vendor/jquery/jquery.min.js"></script>
  <script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>

  <!-- Core plugin JavaScript-->
  <script src="vendor/jquery-easing/jquery.easing.min.js"></script>

  <!-- Custom scripts for all pages-->
  <script src="js/sb-admin-2.min.js"></script>

  <!-- Page level custom scripts -->


  <!-- Vue.js -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.0"></script>

  <!-- Axios -->
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/aspnet-signalr/1.1.4/signalr.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pnotify/2.0.0/pnotify.core.min.js"></script>


  <script src="js/components/sidebar.js"></script>
  <script src="js/components/table.js"></script>

  <!-- Page level plugins -->
  <script src="vendor/datatables/jquery.dataTables.min.js"></script>
  <script src="vendor/datatables/dataTables.bootstrap4.min.js"></script>

</body>

</html>

And finally the relevant component file:

  <div id="logs" ref="logs">
    <div
      v-for="(l, i) in logs"
      :key="i"
      :style="{ color: l[2], fontStyle: l[2] === 'system' ? 'italic' : 'normal' }"
    >
      <time>{{ l[0] }}</time>&gt;
      <span>{{ l[1] }}</span>
    </div>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
  name: 'LogDisplay',
  props: {
    logs: {
      required: true,
      type: Array
    }
  },
  watch: {
    logs () {
      this.$nextTick(() => {
        const el = this.$refs.logs as Element
        el.scrollTop = el.scrollHeight
      })
    }
  }
})
</script>

<style>
#logs {
  background: #444831;
  border-radius: 0.25rem;
  color: #e7e6e1;
  height: calc(100vh - 4rem);
  overflow-y: scroll;
  padding: 1rem;
}

#logs > div {
  padding-bottom: 0.5em;
}

#logs time {
  font-weight: bold;
  padding-right: 0.5em;
}

#logs span {
  padding-left: 0.5em;
}
</style>

Leave a Comment