|
"Bringing sanity to the client side" |
| Home | Demos | Download | Documentation |
|
|
How-To Guide for SmartTables
Calling the constructor
The constructor takes several arguments, many of them optional. If you skip an optional argument you must pass null in its place, but only if you are passing non-null args that come later. The arguments are:
String : id - the id of the html table to use, also will be used as the id of the SmartTable when calling SmartTables.get([id]);
Array : cols - An array of strings for the column names
Array/String: data - A 2-dimensional array or a delimited string
SmartTableParams: (optional) params - An instance of a SmartTableParams object
Array: (optional) metadata - An array of strings for the names of hidden columns.
Object: (optional) paginationRenderer - A replacement for the default pagination renderer
Initiating the Smarttable(s)
Everytime you create a new SmartTable instance, like so:
var st = new SmartTable("testid",cols,data,params,metadata,paginationrenderer);
the instance automatically adds itself to the SmartTables object which is a collection of all live SmartTable instances as well as a manager that gives you some helper methods.
To initialize the SmartTable instances, call the init() function on the SmartTables object, like so:
SmartTables.init();
Using the SmartTableParams object
The SmartTable constructor can take as it's 4th argument an instance of an object called SmartTableParams. If you don't send it the SmartTable will utilize a default version.
The SmartTableParams is simply an object that holds several properties the SmartTable will utilize when rendering.
First, instantiate an instance:
var myParams = new SmartTableParams();
Now, modify any property you like. Here are the properties:
.classNameRoot .active .search .sort .mouseoverhighlightrow .mouseoverhighlightcell .hotswap .serializeDelimiter .sortupind .sortdownind .paginate .rowsperpageBelow I discuss each property. paramsInstance.classNameRoot="smt"The .classNameRoot default "smt". You can change it to whatever you like. If you view the SmartTable.css you'll see that every class name starts with "smt". That is the root. The rest of the name is fixed. See the How-To section on styles for an explanation of all the styles. paramsInstance.search=true;Turns on the default search interface. You can always create your own custom search form. See "How-To make a custom search". paramsInstance.sort=true;Will set all columns to sortable. If you only want certain columns to be sortable, you'll need to set that explicitly using the .setSortable( paramsInstance.mouseoverhighlightrow=false; paramsInstance.mouseoverhighlightcell=false;Will turn on mouseover highlighting of the rows or cells depending on which one is true. If both are true then rows take precedence. paramsInstance.hotswap=false;Editable fields are in "hotswap" mode. This mean they are rendered as plain text, not form fields. Once the use clicks on the text to edit, the text is re-rendered as a form field. See Sample 5. paramsInstance.serializeDelimiter=",";The default delimiter is a comma. If your serialized data is delimited with something different, you need to change the value on the params object. paramsInstance.sortupind="img/up_pointer.png"; paramsInstance.sortdownind="img/down_pointer.png";If you want to use an "up" arrow and "down" arrow when sorting, set the location of those images here. paramsInstance.paginate=true;If you want no pagination, set this to false. paramsInstance.rowsperpage = 20;If you want to show other than 20 rows per page when paginating, set a new value here.
Formatting data
To format the display of your data, you'll need a custom rendering function. The normal rendering function within the SmartTable simply takes the data and sets it as the innerHTML of the TD within the SmartTable.
The inner renderer takes four arguments as the SmartTable is creating each TD. Your custom renderer will have to conform and expect those same arguments.
Array - record: The array of data for the given row.
Number - col: The index of the given column the TD is in.
DOMNode - td: A DOM Node that is a TD
Number - index: This is the index of the row within the full data set.
The default renderer looks like this:
function DEFAULT_RENDERER(record,col,td,index){
td.innerHTML = record[col];
return td;
}
That's it. You might wonder why index is an argument since it's not used. It is necessary for the editmode renderers so that edits can be applied to the correct data row.
To override the default renderer with your own, use the .setRenderer(<colnumber>,<renderer>) function on the SmartTable before initiating.
mySmartTableInstance.setRenderer(3,myRenderer);
Note that you do NOT put quotes around the renderer. You are passing the function itself as an argument.
Here's an example of a simple custom renderer that takes a number value and adds dollar signs and two decimals to it.
function currencyRenderer(record,col,td,index){
td.innerHTML = "$" + record[col].toFixed(2);
}
Making a link
You can, of course, make your own renderer to display data as a link. Or you can use one of
two renderers that are in the SmartTables.js file for convenience.
The SmartTables.js file has two renderers you can use for rendering links. The renderers assume
that there is a hidden column on the data row called "href" and if you want a target name other than "_new" it looks for hidden column called "target". To learn about hidden columns, read that topic in this guide.
To use the link renderer (one if for an email link, one for a regular link), just use the .setRenderer() function on the SmartTable.
mySmartTableInstance.setRenderer(CST_emailRenderer,[colnumber]); mySmartTableInstance.setRenderer(CST_linkRenderer,[colnumber]);Remember, your data records should have a string-indexed value named "href" and an optional string-indexed value named "target". For example:
data[1] = [values......]
data[1]["href"] = "http//:whatever";
data[1]["target"] = "myWindow";
Custom Pagination UI
If you don't like one of the three pagination styles, you can create your own.
The SmartTable uses a separate object to render the pagination, and that is the final optional argument in the SmartTable
constructor. If you don't include that argument the SmartTable uses the DefaultPaginationRenderer.
The pagination renderer object must include two functions that take specific arguments.
function myPaginationRenderer(){
this.id;
this.root;
this.init=function(id,root){
this.id=id;
this.root=root;
}
this.render=function(paginator){
...
}
this.update=function(paginator){
...
}
}
From there you can do whatever you like to render a pagination UI. You have access to the current page, rows per page, total rows, and total pages from the Paginator. View the Paginator API
The SmartTable creates and maintains the Paginator object internally. Upon init(), the SmartTable will call the .render(paginator) function. Every time it paginates, sorts, or re-renders from a search, it will call the .update(paginator) function.
Here is the full, unoptimized source for the pagination renderer that creates a drop down menu, as seen in the Pagination Styles Demo. Note that this code uses a lot of the CST utilities so you should familiarize yourself with the API.
function PaginationRendererC(){
var a = this;
a.TOP=false;
a.BOTTOM=true;
var rowStyle;
var selectMenuStyle;
var trIDRoot;
var tdIDRoot;
a.init=function(id,root){
a.id = id;
a.root = root;
rowStyle = a.root + "PaginationRow";
selectMenuStyle = a.root + "PaginationSelect";
trIDRoot = a.id + "PaginationTR"
tdIDRoot = a.id + "PaginationTD"
}
a.render=function(pag){
var table = dh.b(a.id);
if(table == null) return;
if(a.TOP){
if(table.tHead.childNodes.length == 2){
table.tHead.insertBefore(makeRow(pag,"TOP"),table.tHead.childNodes[1]);
}
else {
table.tHead.appendChild(makeRow(pag,"TOP"));
}
}
if(a.BOTTOM){
table.tFoot.insertBefore(makeRow(pag,"BOTTOM"),table.tFoot.firstChild);
}
}
function makeRow(paginator,type){
var trID= trIDRoot + type;
var tdID = tdIDRoot + type;
var tr = dc.gn("TR",trID,rowStyle);
var td = dc.gn("TD",tdID);
td.colSpan=100;
var totalPages = paginator.getTotalPages();
sl.log("TotalPages = " + totalPages);
var prows = paginator.getRowsPerPage();
if(totalPages <= 1 ) {sl.log("return the tr empty");return tr;}
var label = dc.getNode("SPAN","","","Show records: ");
td.appendChild(label);
var menu = dc.getNode("select",a.id + "PaginationSelect" + type);
td.appendChild(menu);
var label = dc.getNode("SPAN","",""," of " + paginator.getTotalRows());
td.appendChild(label);
menu.className=selectMenuStyle;
var count = 0;
var page = 1;
var curr = 1;
menu.options[count++] = sh.m("1 to " + prows,1);
while(page < totalPages){
var startRow = page * prows+1;
var endRow = startRow + prows-1;
if(endRow > paginator.getTotalRows()) {
endRow = paginator.getTotalRows();
}
menu.options[count++] = sh.m(startRow + " to " + endRow,++page);
}
menu.onchange = function(){
SmartTables.get(a.id).page(sh.a(this));
}
tr.appendChild(td);
return tr;
}
a.update=function(paginator){
var page = paginator.getCurrentPage();
try{
var menuT;
var menuB;
if(a.TOP){
var menuID= a.id + "PaginationSelectTOP";
menuT = dh.b(menuID);
sh.r(menuT,page);
}
if(a.BOTTOM){
var menuID= a.id + "PaginationSelectBOTTOM";
menuB = dh.b(menuID);
sh.r(menuB,page);
}
if(a.TOP && menuT.options.length != paginator.getTotalPages()
|| a.BOTTOM && menuB.options.length != paginator.getTotalPages()){
throw new Error("have ot rerender");
}
}
catch(Error){
sl.log("ERROR:")
for(each in Error){
sl.log(each + ":" + eval("Error." + each));
}
var t = dh.b(a.id);
if(a.TOP){
var trID = trIDRoot + "TOP";
var tr = dh.b(trID);
if(tr != null) t.tHead.removeChild(tr);
}
if(a.BOTTOM){
var trID = trIDRoot + "BOTTOM";
var tr = dh.b(trID);
if(tr != null) t.tFoot.removeChild(tr);
}
a.render(paginator);
}
}
}
Use editing features
You may want to allow users to edit data. Here's how you do it.
To make the values of a given column, editable, just use the setEditMode() function on the SmartTable:
mySmartTableInstance.setEditMode([mode],[colnumber]);
There are three types of editable modes: "text", "select", and "checkbox". For all three types,
the edits aren't saved to the table until the field is no longer in focus, i.e. the user moves the cursor somewhere by clicking or tabbing.
Here's what you
should know about each mode.
text mode:
If the column type has been set to "number" or "date", the SmartTable will automatically validate edits to make sure they are either in number format for numbers, or in "mm/dd/yyyy" format for dates.
select mode:
You'll need to also set a list of value constraints to be used for the select list using
the setEditConstraints([list],[colnumber]) funciton on the SmartTable.
The list argument can be one of two types:
Saving Edits
You've gone through all the work to let your users edit values in a SmartTable, but how do you let them submit those changes to your processor on the server?
Use getEdits or serializeEdits, which are functions on the SmartTable instance.
serializeEdits: returns a delimited string (using a comma or the override value you have set on the SmartTableParams) composed of all the edited rows strung together. Use this one if you plan to simply set it on a form value for a server-side process to parse.
getEdits: returns an array of all the rows that have been edited. Note that it contains the entire row, not just the edited value. You can then manipulate these values to create a url or post however you like.
Example using serializeEdits:
function doSubmit(){
var myHiddenElement = document.getElementById("editedValues");
myHiddenElement.value = SmartTables.get("mySmartTable").serializeEdits();
document.forms.myform.submit();
}
Example using getEdits:
var namecolumn = 3;
function doSubmit(){
var edits = SmartTables.get("mySmartTable").getEdits();
for(each in edits){
var editedName = edits[each][namecolumn]
var newElement = document.createElement("INPUT");
newElement.type = "hidden";
newElement.value = editedName;
newElement.name = "editedName" + edits[each]["id"];
document.forms.myform.appendChild(newElement);
}
document.forms.myform.submit();
}
Here's the last example again, re-written using ClientsideTech utilities shortcuts:
function doSubmit(){
var e = SmartTables.get("mySmartTable").getEdits();
var form = dh.b("myForm");
for(x in edits){
dh.d(form,dc.gi("hidden",e[x][3],null,null,e[x][3]+e[x]["id"]));
}
form.submit();
}
Validating edits
If your column has been set to "number" or "date" types, your "text" mode edits will be validated automatically as a number or a date in "mm/dd/yyyy" format. You can set your own validator if you need to. Here's how.
The SmartTable will validate the fields using a function that takes a value and return true or false. Create your own function that will do this and pass it to the SmartTable instance using the setValidator([validator],[colnumber]) method.
An example that validates a zip code:
function validateZip(val){
if(!isNaN(parseInt(val)) && val.lenght == 7){
return true
}
else{
return false;
}
}
mySmartTableInstance.setValidator(validateZip,"zipCode");
You can set all column-specific properties using the colnumber or the column name;
Modifying CSS styles
One of the parameters on the SmartTableParams is .classnameroot which is the
root of all the css classes in the stylesheet you plan to use for that SmartTable instance.
You'll see in SmartTables.css (found in the download zip) that all the class definitions start with "smt". That is the classnameroot.
You can leave the default value of "smt" and use one of the SmartTable.css files that come with the download zip. Most developers want to use their own styles, so you can either modify all the css definitions within the SmartTable.css or make your own style sheet.
You will definitely want a custom sheet using a different root if you plan on having multiple tables with different stlye. All the style classes that are used by the SmartTable instance when rendering are explained below:
table.smtTable{ .... }
Applies to the table.
TR.smtRow {... }
TD.smtRow {.. }
These apply to each row and each cell in the SmartTable.
TR.smtOdd {... }
TD.smtEven {.. }
These apply if you want something different for odd and even rows. Leave them blank if you want
the rows to be the same.
TR.smtHeader{ ... }
TD.smtHeader{ ... }
These apply to the row containing the column headers and each cell containing a column header.
A.smtHeader{ ... }
A.smtHeader:visited{ ... }
A.smtHeader:hover{ ... }
These apply to the links that are the column headers for sortable columns.
TD.smtSortedColumn { ... }
This applies to the column header cell of a sorted column. If you want no change in style, you
should copy the same rules from TD.smtHeader.
TD.smtButtons{ ... }
TD.smtButtons INPUT { ... }
This applies to the cell and the button within the row that holds the "Search" button when you turn "search" on.
TR.smtHighlightedRow{ ... }
TR.smtHighlightedRow TD{ ... }
This applies to the row and cells within the row when row highlighing is turned on.
.smtHighlightedCell { ... }
This applies to the TD cells within the cell highlighing is turned on.
.smtPaginationRow{ ... }
This applies to the TR row that holds the pagination links when using one of the three
pre-designed pagination styles
.smtPaginationRow{ ... }
.smtPaginationRow TD { ... }
This applies to the TR row and TD (there is just one that spans all cols) that hold the pagination links when using one of the three pre-designed pagination styles
.smtTotalRowsDisplay{ ... }
Applies to the span that holds the pagiantion informational message
.smtPaginationRow A { ... }
.smtPaginationRow A:visited { ... }
.smtPaginationRow A:hover { ... }
Applies to the active pagination links.
.smtPaginationSelect { ... }
Applies to the select menu when using the pagination style that renders a select menu. (The function is called PaginationRendererC)
.smtPaginationRow SPAN { ... }
Applies to the disabled pagination link when using the DefaultPaginationRenderer or PaginationRendererB
.smtEditableSpanMouseover { ... }
Applies to the text in an editable field in hotswap mode.
.smtEditableSpanMouseover { ... }
Applies to the editable text and select fields.
.smtEditableSpanMouseover { ... }
Applies to the editable text and select fields.
.smtSearchButton{ ... }
.smtCancelSearchButton{ ... }
.smtClearSearchButton{ ... }
Applies to the "Search", "Cancel" and "Clear Search" buttons in the Search UI.
Div.smtSearchDiv{ ... }
Applies to the Div that contains the entire search UI. It is nested within a single TD within the TBODY of the SmartTable's TABLE.
Div.smtSearchDivHeader{ ... }
Applies to the top part of the search UI. This Div contains all the search elements.
Div.searchContentDiv{ ... }
Applies to the lower part of the search UI. This Div contains all the search buttons and the error span.
Div.centeringDiv{ text-align: center }
This will center your search UI content. If you dont' want it in the center, eliminate this rule.
.smtSearchOptionsErrors{ ... }
This applies to the span where error messages will be shown. These errors are mostly from entering incorrect data types when searching against numbers and dates.
.smtSearchText{ ... }
.smtSearchSelect{ ... }
These apply to the text and select elements in the search UI.
Hidden columns
The SmartTable will display all values in your row record unless they are string-indexed values.
These string indexed values are hidden columns, undisplayed data. Usually the only reason to include undisplayed data is if you want to use a custom renderer to format the data or display two things at once, or if you want to include an "id" field for when you serialize edits to a post.
If you are using a delimited string as the data, all your undisplayed data must come at the end of a record. You will also need to include an array argument to the SmartTable constructor that is an array of strings which are the names of the string-indexed values. For example:
var myData = "val1,val2,val1,undisplayed1,undisplayed2,val1,val2,val3,undisplayed1,undisplayed2,..."
When you call the constructor you will need to pass an array with "undisplayed1" and "undsiplayed2" as the values for the parameter "metadata". Like so:
var myCols = ["col1","col2","col3"];
var metadata = ["undisplayed1","undisplayd2"];
var smt = new SmartTable('myId',,myData,parameters);
If you are constructing the data as a javascript array instead of a delimited string, you won't need to include the metadata array as part of the constructor. For example:
var count = 0;
var myData = new Array();
myData[count] = ["value1","value2","value3"];
myData[count]["id"] = 1;
count++;
myData[count] = ["value1","value2","value3"];
myData[count]["id"] = 2;
count++
...
var smt = new SmartTable("myId",myCols,myData);
Customize search
Using XML parameters
|