Multiple dojox grids on a single page

I’ve been working on a page that allows an administrator to configure mobile device reimbursement expenses.  The page offers three types of expenses (voice, messages, and data), and five different reimbursement options for each (no reimbursement, flat rate, per unit, per unit with a maximum, and a tiered approach).  There’s also a maximum amount setting to limit the total reimbursement.  For more information about mobile expense reimbursement, visit http://mobiso.com The remainder of this post describes the technical implementation.

I used dojox DataGrids to allow the administrator to set the tiered values.  Each tier has a minimum value and an amount.  The minimum value is the lowest number of units the device must have in order to receive the corresponding amount.  The tier values, both minimums and amounts, must ascend.

To simplify the page submission and display logic, I used hidden textareas to submit the grid data to the server and return it to the client.

The following code is the XHTML  It includes Smarty template code, because it’s inside a loop. I left the Smarty code because it’s used in the javascript to add and remove rows, as well as to move the data between the textareas.  If you only have one or two grids, you could just hard code the indexes or identifiers.

<div class="divAltButtons">
<a href="javascript:AddRow({$smarty.foreach.type.index})">
<img src="images/cp/16x16/actions/edit_add.png" />
</a>
<a href="javascript:RemoveRows({$smarty.foreach.type.index})">
<img src="images/cp/16x16/actions/cancel.png" />
</a>
</div>
<div class="break"></div>
<div dojoType="dojo.data.ItemFileWriteStore" data="storeData_{$sUT}"
jsId="store_{$smarty.foreach.type.index}" id="store_{$smarty.foreach.type.index}">
</div>
<table  dojoType="dojox.grid.DataGrid" id="grid_{$smarty.foreach.type.index}"
store="store_{$smarty.foreach.type.index}" jsId="grid_{$smarty.foreach.type.index}"
clientSort="true"
singleClickEdit="true"
rowSelector="24px"
style="width:200px; height: 117px;"
query="{ldelim}{rdelim}">
<thead>
<tr>
<th field="row" width="40px" hidden="true">Row</th>
<th width="auto" field="minimum" editable="true">Minimum</th>
<th width="auto" field="amount" editable="true">Amount</th>
</tr>
</thead>
</table>
<textarea name="tGridData[{$sUT}]" id="tGridData{$smarty.foreach.type.index}" style="display:none">{$tGridData[$sUT]}
</textarea>

Much of the code came from http://www.dojotoolkit.org/reference-guide/dojo/data/ItemFileWriteStore.html, including itemToJS, which is not listed here.

These functions takes the code from the grid and puts it into the texarea, and gets it back out.

/* Prior to submitting the data to the server, call putGridDataInTextAreas(); to copy the grid data into the text area */
/* When the response is received, use getGridDataFromTextAreas(response); to get the data into the grid */ 

function getGridDataFromTextAreas(response)
{
var i,grid,store,txtArea;
for (i=0;i<iGridCount;i++)
{
txtArea=dojo.byId('tGridData'+i);
txtArea.value=response[txtArea.name];
grid=dijit.byId('grid_'+i); store = new dojo.data.ItemFileWriteStore({jsId: 'store_'+i,data:dojo.fromJson(txtArea.value), urlPreventCache: true});
RefreshGrid(store,grid);
GridFocus(grid);
}
}

function putGridDataInTextAreas()
{
var i,grid,txtArea;
for (i=0;i<iGridCount;i++)
{
grid=dijit.byId('grid_'+i);
GridFocus(grid);
txtArea=dojo.byId('tGridData'+i);
txtArea.value=makeData(grid.store);
}
}

function AddRow(i)
{
grid=dijit.byId('grid_'+i);store=grid.store;
oDefaultItem.row=grid.rowCount+1;
// Blur the current cell to preserve the value
GridFocus(grid);
// set the properties for the new item:
// Insert the new item into the store:
store.newItem(oDefaultItem);
// Set the focus on the new row
grid.focus.setFocusIndex(grid.rowCount-1,0);
RefreshGrid(store,grid);
setDataChanged();
}

function RemoveRows(i)
{
grid=dijit.byId('grid_'+i);store=grid.store;
// Get all selected items from the Grid:
var items = grid.selection.getSelected();
if(items.length){
//if (!confirm(C_confirm_remove)) return;
bRemoving=true;
// Iterate through the list of selected items.
// The current item is available in the variable
// "selectedItem" within the following function:
dojo.forEach(items, function(selectedItem) {
if(selectedItem !== null) {
// Delete the item from the datastore:
store.deleteItem(selectedItem);
} // end if
}); // end forEach
} // end if
RefreshGrid(store,grid);
setDataChanged();
}

function GridFocus(grid)
{
if (!bRemoving)
{
try
{grid.focus.setFocusIndex(0,0);}
catch(err)
{var i=0; /* nop */}
}
bRemoving=false;
}

function RefreshGrid(store,grid)
{
// Refresh the data in the grid
grid.setStore(store);
grid.update();
grid.selection.deselectAll();
}
function makeData(store)
{
var data = [];
for (var i in store._arrayOfAllItems) {
var item=null;
item = store._arrayOfAllItems[i];
data.push(itemToJS(store, item));
}
return dojo.toJson(data, true);
}

/* Create empty grid data */
$aGridData=array();
foreach ($aUsageType as $k => $v)
$aGridData[$v]['items']=array();
/* If there is data in the database, place it in the grid data. Note that there may be several rows of data */
case 'tiered':
$aGridData[$sUsageType]['items'][$row]['row']=$row;
$aGridData[$sUsageType]['items'][$row]['minimum']=$v['usage_minimum_value'];
$aGridData[$sUsageType]['items'][$row]['amount']=number_format($v['method_amount'],2);
$row++;
break;
/* Convert the array to JSON */
require_once 'Zend/Json.php';
foreach ($aGridData as $k => $v)
$aReturn['tGridData['.$k.']']=Zend_Json::encode($v);
/* The code to decode and store the data in the database is application specific, and is not included, it begins with a JSON decode. */

As stated above, the code to extract the data from the textarea and store it is left to the reader.

Key development strategies:

  • Hard code the JSON data to get the grid code running.
  • Use FireBug (FireFox plugin) to examine the data and store.
  • Use var_dump on the server side, frequently.
  • Be sure to sanitize and validate the data, as well as filter it prior to sending it to the client.
  • Deleted rows are submitted as empty braces, so as you loop through the grid rows, discard any empty rows or rows with zero cells of data.
  • Keep an eye on the page load time, it may be good to delay creation of the grid until the page has finished rendering other elements.

This post is courtesy of Lyrix, Inc. http://lyrix.com

Real-Time Apache Server Monitor

Apache includes mod_status, which lets you monitor the server status, in real-time. Click the link above to see it running up at http://apache.org.

There are also ways to automate the reporting, so you can gain further insight into performance, and you should be aware that there may be overhead related to this, but, it’s a great diagnostic tool.

All the configuration is already in /etc/httpd/conf/httpd.conf.

Uncomment the line to enable ExtendedStatus, then the lines referring to server-status, the Location tag and all its contents. Finally, enter your IP address in the Allow directive.

Restart Apache.

Access the status with domain.com/server-status. You can add the refresh= option to auto refresh the display.

And, it’s free.

Psuedo 'Slide Show' - Displaying and Hiding divs

This is a nice example of the setInterval function of javascript.

Key features:

  • Divs can be added without modifying the code
  • Div naming scheme (div#) reduces collisions with other page content
  • setInterval drives the process
  • Tested with IE7 and FF3.5
  • Doesn’t require Flash or other complex content
  • Presentation interface can be managed with CSS

<html>
<head>
</head>
<body>
<!-- Name all the divs with div and a number, they must be sequential and they must start with 1 -->
<!-- You can add more divs as needed, first div is displayed, all others are display:none -->
<div id="div1" style="display:block">One</div>
<div id="div2" style="display:none">Two</div>
<script type="text/javascript">
var loop_pause=2000;
var int=self.setInterval("rotate()",loop_pause);
/* d indicates the current div */
var d=1;
/* newDiv is the one to be shown, firstDiv is the first in the list, and lastDiv is the last one that was shown */
var newDiv,firstDiv=document.getElementById('div1');
var lastDiv=firstDiv;
function rotate()
{
/* The first div is shown on page load, so advance d */
d++;
/* Try to get the next div */
newDiv=document.getElementById('div'+d);
/* If there is no div d */
if (newDiv == null)
{
  /* Cycle back to div1, firstDiv */
  newDiv=firstDiv;
  /* Reset d */
  d=1;
}
/* Hide the previous div */
lastDiv.style.display='none';
/* Display the new or next div */
newDiv.style.display='block';
lastDiv=newDiv;
}
</script>
</body>
</html>

Mobile Device HTML Page - Detect and Redirect

First, the device must be identified so the correct content can be served.

An .htaccess file can be used, or these settings can be placed in httpd.conf (or an included .conf file). This is an Apache 1.3 version, 2.+ may be slightly different.


# Set the MIME type
AddType "application/xhtml+xml;charset=utf-8" .html

# This handles the redirection
RewriteEngine On

# Don't redirect requests for images
RewriteRule \.(gif|jpe?g|png)$ - [L]

# Test for the user agent.  Mozilla is used to indicate a non-mobile device
# A more complex RewriteRule would be required for a production environment
# http://en.wikipedia.org/wiki/List_of_user_agents_for_mobile_phones is a good list
RewriteCond %{HTTP_USER_AGENT} !^Mozilla.*

# Use this page for mobile devices
RewriteRule .* wap.html       [L]

# Otherwise process the request normally

In this case, the page is a very simple page, to let people know that they need to use a browser to visit the page.



<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN"
    "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type"
                content="application/xhtml+xml;charset=utf-8" />
<meta http-equiv="Cache-Control" content="max-age=86400" />
<title>site.com</title>
<style type="text/css">
body
{
font-family:verdana,arial,sans-serif;
text-align:center;
margin:0 auto;
}
</style>
</head>
<body>
  <h1>Welcome to my.site.com</h1>
  <p><img src="icon64x64.gif" alt="logo" title="logo" height="64" width="64" /></p>
  <p>Please visit my.site.com from a laptop or desktop.</p>
</body>
</html>

Validate the code using the link above to ensure it displays well in most devices.

A different approach, using a .jsp is at: https://core.forge.funambol.org/wiki/HowToMakeAMobileOKPageForThePortal, a similar script could be adapted for PHP and ASP.

Fly High - JetScripts

Cool scripts that are worth buying. The purchase price is far less than the cost of the time you’d have to spend to write them yourself, and, in my case, the code’s much better, too. :)

http://jetscripts.com/sanitizer.htm - This is a much improved version of script that’s been shared and used by many people. It protects your code, data, and server. I’m using this on several systems.

http://jetscripts.com/geotool/ - This script allows you to prevent people from various locations from visiting your site. If your target market is the United States, and you don’t sell to or serve other people, there’s no need to serve pages to the rest of the world. You can customize the interface so the message.

http://jetscripts.com/captcha/ - Most people don’t like CAPTCHA forms - the images are difficult to read, and the code can be bothersome to integrate. This one’s easy to read and easy to work with.

http://jetscripts.com/jetbanners.htm - Awesome. Check it out.