Intro to jqGrid Part 3: Columns

OK, in our last two posts we built a basic grid and populated it with data. As you can see, so far it's a very basic grid.

Nothing much to it, really. Let's start adding a few important pieces. jqGrid includes a large set of Column Model Options, but there really aren't a ton that you'll need. Here we'll run through some basics.

First, the id field really isn't something you typically need to show anyone. So, just hide it.

jqGridDemo.js - Column Model - Hide

view plain print about
1colModel: [
2    {name: 'ID', hidden: true},
3    ...

Oh yeah, and the views column is a count of the number of page views. As a number, it should probably be right justified.

jqGridDemo.js - Column Model - Align

view plain print about
1colModel: [
2    ...
3    {name: 'VIEWS', align: 'right'}

Great! But that column is way too wide! Without any additional info, jqGrid will attempt to size the columns according to their data, and currently it's just making three even columns. Let's size it down.

jqGridDemo.js - Column Model - Width

view plain print about
1colModel: [
2    ...,
3    {name: 'VIEWS', align: 'right', width: 60, fixed: true}

Alright! We've set a 'fixed' width, so that any resizing of the grid (even automatic resizing) will maintain the set column width. We set it to give full width of the column title, as well as some room for the sort markers when the column is being sorted.

Now let's talk about three options that can be somewhat confusing: index, label and name. Up until now we've used the name option, which has mirrored the column name being returned. However, we might want our column header to be different than the actual column name. For this, we use the label option.

jqGridDemo.js - Column Model - Label

view plain print about
1colModel: [
2    ...,
3    {name: 'POSTED', label: 'Release Date'}

This changed the label used in the column header, while maintaining a reference used when sorting the grid by the posted field. This is good, until you do something like this:

jqGridDemo.js - Column Model - Remap

view plain print about
2    gridCols['ID'] + gridMultiSelect,
3    gridCols['ID'] + gridMultiSelect,
4    gridCols['TITLE'] + gridMultiSelect,
5    gridCols['POSTED'] + gridMultiSelect,
6    gridCols['VIEWS'] + gridMultiSelect

jqGridDemo.js - Column Model - Index

view plain print about
1colModel: [
2    {name: 'ID', hidden: true},
3    {name: 'Action', index: 'ID', label: 'Action', width: 80, fixed: true, sortable: false, align: 'center'},
4    {name: 'Title'},
5    {name: 'Posted', label: 'Release Date'},
6    {name: 'Views', align: 'right', width: 60, fixed: true}

"Cutter, What are you doin' ta me!?!" Yeah, now it's confusing. I've added a column. A column that also references the ID field in the return dataset. In this instance the index really isn't truly necessary, but I'll try to explain it for you anyway. Up until now, jqGrid has used the name option as the value that is passed back to the server on a sort request. Here's the thing though: each column has to have a unique reference. That's what the name option is for; being a unique column reference within jqGrid. So, if you have two columns whose underlying data is the same (as it shows you in our new Remap config), then you need a unique reference for jqGrid (the name), and the index field reference that jqGrid will send back to the server on sort requests (again, with the sortable: false I've thrown in here, it's really moot for us). So, to recap:

  • name - A unique column reference used by jqGrid
  • index - A data field reference used in sort requests. If not present then the name is used.
  • label - If present it will override the name option, for what to display in the column header.

You probably noticed that I added a little something to our column remap code.

view plain print about
1gridCols['ID'] + gridMultiSelect,

This goes along with a new variable I added to our global variable declarations at the very top of our script.

view plain print about
1var gridCols = {set:false},
2    gridMultiSelect = 0;

I'll probably not use that on this round, but it will become important, so I'll leave it for now.

Column Formatting

Now that we've talked about some of the more important column options, let's get into column formatting. Now that we've added some configuration you'll notice a new Action column. Right now, if you ran your template, you'd see a truncated ID value in the cells. We'll need that ID in our output, but the Action column we're building will be used to display action icons (edit, delete, etc). jqGrid has functions for doing this, if you're using it's edit packages, but my app has custom editors for a lot of this data, so we'll apply a custom column formatter to show these action icons.

jqGrid provides predefined formatters for many things, but you can also create your own custom formatters to create your own cell templates. A custom formatter is just a function, applied through the column model, that returns the string to be displayed in the cell. Your function will take three arguments, cellvalue, options, and rowObject. The cellvalue is the value of the data that jqGrid is trying to apply to the cell (in our case, a record's ID). The options is an object containing the rowId and the colModel of the record being applied. The rowObject is the data for the entire row of the record being applied.

jqGrid provides the ability to apply these functions as extensions of it's built in formatter package. Let's write a basic actionFormatter function that returns just the first two characters of the ID field, to get started.

jqGridDemo.js - actionFormatter - figure 1

view plain print about
1$.extend($.fn.fmatter, {
2    actionFormatter: function(cellvalue, options, rowObject) {
3        return cellvalue.substr(0,2);
4    }

jqGridDemo.js - Column Model - Custom Formatter

view plain print about
1colModel: [
2    ...
3    {name: 'Action', index: 'ID', label: 'Action', width: 80, fixed: true, sortable: false, align: 'center', formatter: 'actionFormatter'},
4    ...

That was easy! You see now how we get to value being applied to the cell. Now let's really change it up, by applying the custom output we discussed before. First, we need the style references to the icons we're going to use.

jqGridDemo.css - icons

view plain print about
1/* Basic layout of all trigger icons */
2.icon-trigger { margin: 2px; vertical-align: middle; display: inline-block; width: 16px; height: 16px; }
3.action-trigger { cursor: pointer; }
4.disabled-trigger {opacity:0.4;filter:alpha(opacity=40)!important;}
6/* delete icon image for trigger */
7.delete { background: url('/resources/images/icons/delete.png') no-repeat scroll 0px 0px transparent !important; }
9/* pencil icon image for trigger */
10.pencil { background: url('/resources/images/icons/pencil.png') no-repeat scroll 0px 0px transparent !important; }

For our demo, I'm using the highly useful FamFamFam Silk icon library. Here I've defined some classes for the display of icon 'triggers', or icons that are used as buttons for actions. Next, we'll adjust our actionFormatter to apply the proper output.

jqGridDemo.js - actionFormatter - figure 2

view plain print about
1$.extend($.fn.fmatter, {
2    actionFormatter: function(cellvalue, options, rowObject) {
3        var retVal = "<span class=\'icon-trigger action-trigger pencil\' rel=\'" + cellvalue + "\' \/>";
4        retVal += "<span class=\'icon-trigger action-trigger delete\' rel=\'" + cellvalue + "\' \/>";
5        return retVal;
6    }

As you can see, now when you re-run your template you have a nice, formatted Action column, with action icons for 'edit' and 'delete'.

So, in this post we covered some of the more important Column Model display options, as well as creating a custom column formatter. In our next entry we'll tie some functions to our 'action icons', and talk about row selection options. You can find sample code attached in the Download link at the bottom of the page.

Why I Like

With the position I'm in now, I've had the joy of finally being able to write (full time) for ColdFusion 9. There were many great improvements and updates within CF 9, but two of mine were the increased parity to tags, and the ability to write fully scripted components. I was one of the first to post a fully scripted Application.cfc. I've pushed most of my code examples, since then, using cfscript. I wrote a brief primer on extending ColdFusion, by writing custom components for use server wide. I use cfscript constantly in my day to day development, and used to use as much as I could before CF 9. And yet three times, in as many weeks, people have asked me "Why?"

I've gotten this question before. Most, who've mentioned it, don't really mind it one way or another, they just wanted to know why it's my preference. Others were adamantly opposed to it, because it is so different in style from other scripted languages. There have been efforts to improve cfscript, and even calls to deprecate cfscript, in it's current form, and reinvent it (or switch wholly to server side ActionScript). Last month, Adam Tuttle created the CFCommunity, on GitHub, for open source contributions in filling in the few remaining gaps in cfscript through custom component objects. But, why do I like it?

I've been a ColdFusion developer now for over a decade. Like every other programming language I've worked with I was self taught, and came to CF after learning Visual Basic (6), some C++, and InstallScript (which is what Install Shield used to use for custom installers). Aside from progressively improving my knowledge and usage of HTML/XHTML and CSS, I was primarily a 'server side' developer for much of the beginning of my CF career. I'm a programmer, not a designer, so I wrote (hopefully) good logic. The browser wars had soured me on JavaScript, so I avoided it when I could. Luckily libraries like ExtJS and JQuery came on the scene. By this point my grasp of ColdFusion (and programming, in general) was much more advanced, so putting effort into interface development and usability was a good step. This also meant heavily re-aquainting myself with JavaScript, and remembering what a joy scripting was.

After a while, I found that I was writing more JavaScript than ColdFusion, and focusing more time on client-side development than on the server. Complex server-side code had become second nature, and came rather quickly, whereas client-side code was still relatively new and challenging. I was writing two to three lines of client-side code for every line of ColdFusion (at least). In the process, I've also had to adjust how I approach some of my ColdFusion development, to accomodate ajax interface development. It's been a lot of fun.

While I love ColdFusion, writing complex logic in JavaScript again had shown me how much more rapid scripting was. While ColdFusion's tag syntax is fantastic for transitioning html developer's, and great for generating client-side code (cfoutput and cfloop stuff), straight scripting syntax for logic is much faster, trimmer, and concise. I will agree with those who say that cfscript needs an overhaul. Function naming is inconsistent, as is treatment of arguments and argument naming. All of that aside, it's still quick, trim, and powerful, and the Java behind the functions is (sometimes) more current, and better performant, than the Java behind their tag counterparts.

I wish Adam luck with his CFCommunity project on GitHub. I think it will be a great thing for the ColdFusion community, as a whole, if it can gain steam, and hope to make some time to help contribute to it myself. I also hope that, in some future version of ColdFusion, that the Adobe engineering team will do a thorough review of cfscript and work to create a scripted ColdFusion that the community, as a whole, can agree upon as a new standard. In the meantime, I'm happy to use what we have, as it's still very performant, and a lot of fun!

Intro to jqGrid Part 2: Getting the Data

As I mentioned in an Intro to jqGrid, sometimes you have to deal with remote data that isn't in the 'standard' JSON recordset format. I also like to reuse my server-side code, and prefer not to unnecessarily hack native data to meet a client-side need. For this reason, it is often necessary to write client-side methods that parse the server-side native format. Unfortunately, many JQuery plugins only handle the 'standard' JSON recordset format:


Intro to jqGrid

While there are better choices (ExtJS for instance) for component and architecture libraries, some time ago someone entrenched our current system in JQuery and JQueryUI. But, since JQueryUI is already so prevelent within the system, it would be very time consuming to replace it. JQueryUI isn't bad, by any means, just incomplete, from the standpoint of building "applications". "But, JQueryUI is the bomb! How can you say such things?" Well...