Javascript: Introduction to the Ext Grid Object

torstaina, helmikuuta 07, 2008

Grid controls get used in lots of user interfaces and thankfully, Ext makes it pretty easy to use a normally cumbersome component. It takes only a few relatively simple lines to create the control and most of them are configuration parameters that leave little room for bugs to appear later. To illustrate the point, let’s take a look at a small grid that has three populated data columns and one calculated column.
We have four columns: name, hours, rate and cost. The cost column is calculated by multiplying the rate and hours columns. This is pretty simple stuff. We’ll make it more interesting by letting you change the values of the rate andhour columns and automatically update the cost column.
Let’s first look at the grid and its children. The GridPanel class allows you to show data only. The EditorGridPanel allows you to configure a selectively editable grid control. What I mean is that you can configure a specific editor for each column in the grid. You can use any of the form package’s Field objects as an editor.The PropertyGrid is derived from the EditorGridPanel and let’s you manage a grid of key/value pairs. We will focus on the GridPanel andEditorGridPanel in this discussion.
We can see from the class diagram that a GridPanel has a ColumnModelfield which is a collection of column configurations. The ColumnModel.renderermember lets you change the default rendering behavior of a column. TheColumnModel.editor member is an Ext.form.Field object, such as TextField orNumberField. GridPanel ignores the editor member, while EditorGridPaneluses the editor to allow the user to modify a cell’s contents.
So how does a user edit a cell? She clicks on it one or more times. You can set the number of clicks (single, double, etc.) during the configuration of theEditorGridPanel by setting the clicksToEdit field. The default number of clicks is two.
A GridPanel is backed by an Ext.data.Store object, which contains the data for the rows of the grid. There are several different kinds of stores for loading and managing various data sources. The relationship between a GridPanel and aStore is very tight: modify a value from the either the grid or the store and the other will immediately update.
This intimacy between the two objects is very helpful when managing calculated fields. Consider for a moment that the data is being loaded remotely, as it would be in a real application, and that the rate and hours columns are populated by the server and locally calculating the cost column when either thehours or rate columns change. There are several ways to accomplish this.
The first method is to register for the afteredit event, which fires after a cell is edited. Your event handler calculates the values of your calculated fields and saves them to the store. Remember, the grid will reflect the changes to the store so the grid will display the new cost value. A subtlety is that the store can load the cost column already populated by the server.
The second method is that you define a renderer for a calculated field and not bother to update the store. A row gets rendered whenever a cell is modified. I use this method in the example, however, I believe the first method is more useful.
Why write an example that uses an odd implementation? Both work well. The second implementation doesn’t require the calculated field to be populated by the server. I can simply calculate its value on the fly. It will perform more poorly if I have a lot of rows though. It also implies that the server doesn’t store the calculated field. The first method obviously implies that the server has a representation of the calculated field and that the grid should render more quickly because it has fewer calculations to perform.
The last thing I will mention about the Store class is that it has a kind of transaction management. You can make a series of changes and commit them by calling Store.commitChanges() or reject them by callingStore.rejectChanges(). The rejectChanges() method is handy for undoing mistakes.
Let’s look at the example application’s code now. The interesting sections have a red number in the comments and following explanation will refer to them.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Simple Ext EditorGrid Example</title>
<link rel="stylesheet" type="text/css" href="./ext-2.0.1/resources/css/ext-all.css"/>
<script type="text/javascript" src="./ext-2.0.1/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="./ext-2.0.1/ext-all-debug.js"></script>
<script>
function main() {
// 1. Create some dummy data data
var sampleData = [
[ 'Bob', 20, 13.45 ],
[ 'Bill', 40, 12.92],
[ 'Mike', 45, 23.02]
];

// 2. Create the Store
var store = new Ext.data.SimpleStore({
fields : [
{ name: 'name', type: 'string' },
{ name: 'hours', type: 'int' },
{ name: 'rate', type: 'float' },
{ name: 'cost', type: 'float' }
],
data: sampleData
});

// 3. Initialize the grid control
var simpleGrid = new Ext.grid.EditorGridPanel({
store: store,
columns: [{
header: 'Name', // 4. Field cannot be edited.
width: 160,
sortable: false,
dataIndex: 'name'
},
 {
header: 'Hours', // 5. Field can be edited
width: 75,
sortable: false,
dataIndex: 'hours',
editor: new Ext.form.NumberField({
allowBlank: false,
allowDecimals: false
})
},
 {
header: 'Rate', // 5. Field can be edited
width: 75,
sortable: false,
dataIndex: 'rate',
editor: new Ext.form.NumberField({
allowBlank: false,
allowDecimals: true
})
},
 {
header: 'Cost', // 6. Calculated field
width: 75,
sortable: false,
renderer: formatCost,
dataIndex: 'cost'
}
 ],
clicksToEdit: 1,
stripeRows: true,
autoHeight:true,
width:500,
enableHdMenu: false, // 7. Disable the context menu
title:'Editor Grid',
renderTo: 'formDiv'
});
}

//8. Formatting function for the cost column
function formatCost(value, metadata, record, rowIndex, colIndex, store) {
var results = getCost(record);
return Ext.util.Format.usMoney(results);
}

//9. Calculates the cost
function getCost(record) {
var hours = record.get('hours');
var rate = record.get('rate');
var results = hours * rate;
return results;
}
</script>
</head>
<body>
<div style="margin: 10px 10px;">
<div id="formDiv"/>
</div>
<script>
Ext.onReady( main );
</script>
</body>
</html>
The code creates some sample array data(1) and a Store(2) to map the array into name-value pairs for each row. Then we create a new EditorGridPanel(3) with four columns: 1 non-editable(4), 2 editable(5) and 1 calculated(6). Note thedataIndex member in each column configuration that maps to a name in theStore’s configuration. This mapping allows the grid to lookup and update values in the store. We also set a formatter for the cost column. TheformatCost(8)method does the calculation by calling getCost(9) and then changes its formatting using the Ext.utilFormat.usMoney() method. Theformatter gets called whenever the row gets updated, including during its initial population.
It may seem like I glossed over a few important points, however, there isn’t very much to tell for this example. Most of the code is for the configuration of theEditorGridPanel and there are very few odd gotcha type things. Ok, there is one odd gotcha, but I haven’t tried it recently and don’t know if Ext 2.01 even suffers from it. The problem was that a grid would sometimes take up the entire width of the browser regardless of its container. Hardcoding the width gets around that problem.
That concludes our discussion of the Ext grid component for today. We’ll talk about it more though in the coming weeks with more interesting examples.

You Might Also Like

0 comments