California Ajax Solutions Team

www.CalAST.com

The CheckboxTable Object

Kenneth F. Greenberg

In this article, I will describe a useful JavaScript object for presenting a site user with the results of a query in tabular form, and then allowing that user to choose one or more items for further action. The CheckboxTable is a variant of the SelectorTable object, and it probably helps to read this article describing the SelectorTable first. In general terms, a CheckboxTable is a table, probably filled in from a database query, where the first column consists of checkboxes that are supplied by the object - the rest of the displayed data is supplied by the application. (Note: at present, the CheckboxTable does not support paging.)

At first glance, you might conclude that there isn't much difference between a SelectorTable and a CheckboxTable, other than the possibility of multiple selection provided by the latter. Of course, you would be correct, but there are some interesting side effects of this difference. One of these should be obvious - you want some mechanism to "walk the table" and return the next selected object. One that I did not anticipate (but one of my customers did) was the desire to sometimes make a CheckboxTable work like a SelectorTable under some conditions. So, the CheckboxTable was extended to provide some minimal support for the idea of a currently selected item, which is valid only when exactly one box is checked.

Let's take a look at a real world example of how this could be used. Supposed you wish to manage a list of candidates for employment. Each line in the table would represent a person, but the table provides only a summary of information about each candidate - there's way too much information about people to present it all at once. So, one thing you might want to do is send email to a group of people for whom you are missing a particular piece if information. You provide a button labeled Send Email and you attach an event listener to it. The event listener walks the table and, for each person whose checkbox is selected, sends out a message "Thanks for applying, but you haven't sent in the names of references yet" - or whatever is appropriate. But since you have the list of candidates in front of you, you might want to bring up the resume for one of the candidates. This requires the the use of the method that returns the selected item when there is only one.

If you read the article on the SelectorTable, most of this will seem familiar, so I will try to avoid repeating all the same information. Instead I will point out the differences as we go along.

The first step is, as always creating an instance of the object:

var cbt = new CheckboxTable('candidates');

We then add a header in the usual way:

cbt.addHeader('Last','First','City','State','Phone','Email');

We can add people to the table in either of the two ways described in the SelectorTable article - just the information, or the information with an ID for later processing of the selected rows. Here is the first difference with the new object. In a CheckboxTable, every checkbox has a value. If you add rows without a specific value, the value corresponding to the row index (starting from zero) is set as the value of the checkbox input element. Otherwise, your specified value will be assigned. This was not really a conscious decision, so much as a requirement of the application for which this object was developed. The more common usage would be to specify a particular ID for each person in the table, since they probably are in a database table somewhere and the ID represents the primary key of that table.

cbt.addRowWithValue(16,'Montana','Joe','San Francisco','CA','415-555-4949','hallOfFameQB@somewhere.com');

So, you now have a nice table structure (after adding a few more rows), but how should it best be used? One of the techniques I use is to create two divs, one for the table and one for a button bar that runs underneath it. That way, as the web application changes in response to user input, we can redraw the buttons to provide appropriate actions for the current state. So, given that we have:

<div id='tableDiv'></div>
<div id='buttonBar'></div>

Then we can add the fully populated table to the table div and paint some buttons below it.

cbt.appendTo(document.getElementById('tableDiv'));

Choosing the buttons is completely dependent on the web application, but let's use the input element variation (described elsewhere) to create one. We will create a new button, assign it an onclick event listener, and add it to our button bar DIV.

var emb = new InputElementButton('emb','Send Email');
emb.onClick(sendMailToSelected);
emb.appendTo(document.getElementById('buttonBar'));

Of course, we need to provide the event listener for the onclick event, which will actually do something useful with the information in the table object. What it should do is fire off an email to every person who is "checked." We can assume there is a function sendMessage defined elsewhere, and we only have to pass it the ID of any selected recipients.

function sendMailToSelected() {
    var nextSelected;
    cbt.resetIndex();
    if (!confirm('No undo - are you sure?')) { return; }
    while ((nextSelected = cbt.getNextSelectedValue())) {
        sendMessage(nextSelected);
    }
}

Let's look at what's happening in this event listener. We define a variable to hold the ID of the person who has been selected. The object contains a moving pointer that steps through the table looking for a checkbox that has been selected. In case this function has been called more than once, we make sure we point to the beginning of the table by calling the resetIndex method. We then repeatedly call the getNextSelectedValue method. It returns null if there are no more selected entries (or if there never were any, or if the table was empty). But if there is another person selected, then it returns the ID associated with that person, stored as the value of the checkbox when the table was built. We can then pass that back to the server for action. Each call starts the search one past the last result, until the end of the table is reached.

Earlier, I mentioned that these objects can behave a bit like SelectorTable objects (which can have only one item selected at a time) as required. We accomplish this by defining a getSelectedValue method that looks pretty much the same as the one for a SelectorTable object. The difference here is that if there are multiple lines selected, then this method returns null. Here's how we use it. In our example, we will suggest that there is a server-side script that provides a number of details about this candidate in a new tab (or a new instance of your browser). But we only allow this to happen when one candidate is chosen from the table. This is another event listener, invoked when the user clicks on the "Show Details" button.

function details() {
    var id = cbt.getSelectedValue(); // null if multi select
    if (!id) {
        alert('You must select exactly one candidate to use this button!');
        return;
    }
    var url = 'candidate_details.php?id='+id;
    window.open(url);
}

There are also a few convenience methods that may be tied to event listeners as desired. For example, it is often useful to select everything in the table except for one item, so we provide a selectAll method that should allow the user to accomplish this with two clicks if the user interface you design supports this sort of activity.

Application Programming Interface

Our preference is to use the jsdoc document generator, as it builds HTML documentation directly from the source code. Thus, as new functions are added or existing ones are changed, up-to-date documentation is immediately available, and the possibility of forgetting to update the documentation to match the code is minimized. The documentation for the CheckboxTable object may be found here.