Now I have my data, but to connect it to the UI, I'm going to use a framework.

I've had some good experiences with vue.js in the past: it's easy to get started without much fuss, but broadly used and well supported.

As the most basic feature of my web app, I just want to show the data that I receive from the Sheet.

Load Vue.js

But to do that, I already want to get my UI framework in place, so that I can continue extending the app from there.

Add Vue.js as a reference

Checkout the Getting Started guide for vue.js if you're going to use it.

For now, I'll add this to my index.html head:

<script src="https://cdn.jsdelivr.net/npm/vue"></script>

Create the Vue App

To make sure everything is loaded, I will follow the getting started guide to add a message, in index.html:

<body>
  <div id="app">
    {{ message }}
  </div>
</body>

And in js/app.js:

var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
});
Note: for this to work I had to move the <script> tag to include my js/app.js to the bottom of the <body> tag

In summary, index.html:

<html>
<head>
  <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>

<body>
  <div id="app">
    {{ message }}
  </div>
  
  <script src="js/app.js"></script>
</body>
</html>

And js/app.js:

let url = 'https://spreadsheets.google.com/feeds/cells/16hJ_67ox89L3DuVuc8KWAGMYFwb58rVP2fjCkc2AoGE/1/public/full?alt=json';

fetch(url)
  .then(
    function(response) {
        response.json().then(function(data) {
          console.log(data);
        });
    }
  );

var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
});

Load data when Vue JS app loads

Currently, I fetch the data and only write it to the console.

What I want is to tell the vue app to load the data, and once it's loaded make it available to views inside the vue app.

Remember I'm making this app as a prototype. I may not be using vue in the ideal manner. Just a way to get it going. I may change this later.

What I can do is:

  • Add a flag to the app data indicating if the data is available or not
  • On the "created" hook of the app, actually load the data

I can do this as follows in the app:

// ...
var app = new Vue({
  el: '#app',
  data: {
    dataAvailable: false,
    data: {}
  },
  created: function () {
    fetch(url)
    .then(
      function(response) {
          response.json().then(function(data) {
            app.data = data;
            app.dataAvailable = true;
          });
      }
    ).catch((error) => {
      console.error('Error:', error);
    });
  }
});

Note, I replace the message from the original with my new variables: dataAvailable and data.

I can change my html as follows:

  <div id="app">
    Data available: {{ dataAvailable }}
    <div v-if="dataAvailable">
      HERE COMES THE LIST!
    </div>
  </div>

Now I have a page that indicates "loading: false" as soon as it's loading, and "loading: true" as soon as the data is available.

The second div is only rendered when the data is available. The plan is to put some markup in there to actually list the values in a table.

Show a list with vue

The data structure coming from the Sheet offers a property .feed.entry which is the array of cells with data.

It's a little annoying that the things are structured in a flat list of cells. So I'm adding a method to my app that will restructure the data as a list of rows:

var app = new Vue({
// ...
  methods: {
    getRows: function () {
      // group by row
      let rows = app.data.feed.entry.reduce((acc, reducer) => {
        let row = acc[reducer.gs$cell.row - 1] || { row: reducer.gs$cell.row, cells: [] };
        row.cells.push(reducer.gs$cell);
        acc[reducer.gs$cell.row - 1] = row;
        return acc;
      }, []);
      
      return rows;
    }
  }
});

The reduce call is used to group by row. It's not the most elegant implementation, but the result is an array of two rows, each with 14 cells.

I can now continue to render an HTML table based on the data:

  <div id="app">
    Data available: {{ dataAvailable }}
    <div v-if="dataAvailable">
      <table>
        <tr v-for="row in getRows()">
          <td v-for="cell in row.cells">
            {{ cell.inputValue }}
          </td>
        </tr>
      </table>
    </div>
  </div>

It looks ugly, but that displays my data on screen:

Conclusion

Here I added vue js to my app. This will allow me to more easily drive my UI from the data in my app.

I now have a vue app that, when available, shows the loaded data from the Google Sheet as an html table on the page.

Next up is adding something to make the page look better, without doing too much design / css work.