Mad, Beautiful Ideas
Adding Columns to a YUI2 DataTable

In the new Schedules of Classes at Washington State University, I recently added Google Maps integration for showing where buildings are located on our campuses. One of our pages is simply a list of the buildings that are in our system. However, I wanted the mapping code to use progressive enhancement based on whether or not Latitude and Longitude data is available, and if the user is using a browser that will support the APIs that I'm using.

In the first version of the Buildings page, I had decided to use YUI2 DataTable, since YUI3's was not available when I began, and it doesn't yet support progressive enhancement as I would like. Using DataTable provided me with the ability to sort the data based on user preference, and should enable me to add filtering code in the near future trivially, though I am evaluating different YUI3 DataTable implementations to see if they'll work for my needs.

The raw HTML table the datatable is based on doesn't include the Latitude and Longitude data, nor do I think it should be, which means that once that data becomes available (either downloaded from the server or pulled from the browser's local storage), I need to add that data to the table, and show the user an indicator whether or not building data is available. A YUI2 DataSource does not provide, as far as I can tell, a trivial way to add a column to it's internal storage, which in my case was an HTML Table.

My source data was a simple JavaScript object, keyed off the first column in my table. Adding the rows required simple DOM manipulation, though for my example, I have wrapped the DOM TR Node in a YUI3 Node instance to get access to YUI3 sugar.

        new Y.Node(source.liveData.tBodies[0]).get('rows').each(function(row) {
          var
              // Get the trimmed value of the Abbreviation
              abbr = row.one('td').get('text').replace(/^\s*([\s\S]*\S*)?\s*$/, "$1"),
              latlong = data[abbr], 
              // Test if latlong exists, and that it defines a Longitude and Latitude
              value = latlong && latlong.Longitude && latlong.Latitude;
          row.append("" + value + "");
        });
        source.responseSchema.fields.push({ key: "map" });
        

After the above code runs, there is a new column in all my data that is either 'true' or 'false' depending on whether or not the 'data' object contains latitude and longitude data, and the data will be available under the 'map' key on the datasource. Next, I need to add this column to the DataTable.

        datatable.insertColumn({ key: "map", label: "Map?", formatter: function(el, oRec, oCol, oData) {
            if (oData === "true") {
                el.innerHTML = "Map Available";
            } else {
                el.innerHTML = "No Map Available";
            }
        }, sortable: true});
        

Running this will immediately add the column to the datatable, but the column will be blank in every row, becuase it the datatable hasn't reparsed the datasource. This is where things got more difficult, as I could not locate an 'updateData' method (or analogue) on the DataTable prototype. Solving this required me to dig into the source for DataTable to see how it processed the source table on the initial load.

        source.sendRequest(null, {
          success: datatable.onDataReturnSetRows,
          failure: datatable.onDataReturnSetRows,
          scope: datatable,
          argument: datatable.getState(),
          null
        });
        

The first argument to sendRequest can be null, because I simply want to re-parse my liveData I updated earlier, while the second argument contains the methods on datatable that will cause my datatable to properly update, and making sure the execute in the proper scope. The final arugment has been replaced by the 'scope' decleration in the second argument, and should remain null in current and future YUI2 datatable code.

I hope that YUI3 DataTable will have an easier way to force a refresh of the DataSource after I've modified the data, whether that is via a 'changed' event on the DataSource or just a simple method which will perform the update. These may already exist, but without PE support, I haven't really bothered to investigate it.