devxlogo

Building a Simple GridPanel in Ext JS

Building a Simple GridPanel in Ext JS

hen implementing GridPanels, I typically start by configuring the DataStore. The reason for this is because the configuration of the ColumnModel is directly related to the configuration of the DataStore. This is where we’ll start too.

Editor’s Note: This article is excerpted from the book Ext JS in Action by Jesus Garcia, published by Manning Publications, ISBN 9781935182115.

Setting Up an Array DataStore

In the following example, we’re going to create a complete end-to-end DataStore that reads data already present in memory. This means that we’re going to instantiate instances of all of the supporting classes from the Proxy to the Store. This exercise will help us see the working parts being configured and instantiated. Afterwards, we’ll learn how to use some of the pre-configured DataStore convenience classes to make constructing certain types of stores easier with much less code.

Listing 1. Creating a DataStore that loads local array datavar arrayData = [                                                      (1)     ['Jay Garcia',    'MD'],     ['Aaron Baker',   'VA'],     ['Susan Smith',   'DC'],     ['Mary Stein',    'DE'],     ['Bryan Shanley', 'NJ'],     ['Nyri Selgado',  'CA'] ];  var nameRecord = Ext.data.Record.create([                            (2)     {  name : 'name',  mapping : 1  },     {  name : 'state', mapping : 2  } ]);  var arrayReader = new Ext.data.ArrayReader({}, nameRecord);          (3)  var memoryProxy  = new Ext.data.MemoryProxy(arrayData);              (4)  var store = new Ext.data.Store({                                     (5)     reader : arrayReader,     proxy  : memoryProxy });{1} Creating local array data{2} Using Ext.data.Record.create to create a constructor for an Ext.data.Record{3} Instantiating an ArrayReader{4} Constructing a new MemoryPRoxy{5} Building our Store

In the above listing, we implement the full gamut of DataStore configuration. We first start by creating an array of arrays, which is referenced by the variable arrayData {1}. Please pay close attention to the format the array data is in, as this is the expected format for the ArrayReader class. The reason the data is an array of arrays is because each child array contained within the parent array is treated as a singular record.

Next, we create an instance of data.MemoryProxy, which is what will load our unformatted data from memory and is referenced by the variable memoryProxy {2}. We pass in the reference arrayData as the only argument.

Next, we create an instance of data.Record {3} and reference it in the variable nameRecord, which will be used as the template to map our array data points to create actual records. We pass an array of object literals {4} to the Record.create method, which is known as the ‘fields’ and details each field name and its mapping. Each of these object literals are configuration objects for the Ext.data.Field class, which is the smallest unit of data managed data within a Record. In this case, we map the field ‘personName’ to the first data point in each array record and the ‘state’ field to the second data point.

NOTE: Notice how we’re not calling new Ext.data.Record(). This is because data.Record is a special class that is able to create constructors by using its create method, which returns a new record constructor. Understanding how data.Record.create works is essential to performing additions to a DataStore.

We then move on to create an instance of ArrayReader {5}, which is what’s responsible for sorting out the data retrieved by the proxy and creating new instances of the record constructor we just created. From a 30,000 foot view, the ArrayReader reads each Record, it creates a new instance of nameRecord by calling new nameRecord, passing the parsed data over to it, which is then loaded to the store.

Lastly, we create our DataStore for which we pass the reader and proxy we created, which completes the creation of our array DataStore. This completes our end-to-end example of how to create a store that reads array data. With this pattern, you can change the type of data the store is able to load. To do this, you swap out the ArrayReader with either a JsonReader or an XmlReader. Likewise, if you wanted to change the data source, you can swap out the MemoryProxy for another such as the HttpProxy, ScriptTagProxy or DirectProxy.

Using convenience classes can make our lives a little easier. If we want to recreate the Store above using the ArrayStore convenience class, this is what our code would look like using our arrayData from above.

  var store = new Ext.data.ArrayStore({    data   : arrayData,    fields : ['personName', 'state']  });

Just as we see in the above example, we use shortcut notation for the fields to create an instance of Ext.data.ArrayStore. We achieve this by passing a reference of the data, which is our arrayData, and a list of fields, which provide the mapping. Notice how the fields property is a simple list of strings? This is a completely valid configuration of field mappings because Ext is smart enough to create the name and index mapping based on the string values passed in this manner. In fact, you could have a mixture of objects and strings in a fields configuration array. For instance, the following configuration is completely valid:

   fields : [ 'fullName', { name : 'state', mapping : 2} ]

Having this flexibility is something that can be really cool to leverage. Just know that having a mixture of field configurations like this can make the code a bit hard to read.

Using this convenience class saved us the step of having to create a proxy, record template and reader to configure the store. Usage of the JsonStore and XML Store are equally as simple, which we’ll learn more about later. Moving forward, we’ll be using the convenience classes to save us time.

For now we’ll move on to creating the ColumnModel, which defines the vertical slices of data that our GridPanel will display along with our GridView component.

Courtesy of Manning Publications. All rights reserved.

Completing Our First GridPanel

As we discussed before, the ColumnModel has a direct dependency on the DataStore’s configuration. This dependency has to do with a direct relationship between the data field records and the column. Just like the data fields map to a specific data point in the raw inbound data, columns map to the record field names.

To finish our GridPanel construction, we need to create a ColumnModel, GridView, SelectionModel, and then we can configure the GridPanel itself.

Listing 2. Creating an ArrayStore var colModel = new Ext.grid.ColumnModel([                    (1)     {         header    : 'Full Name',         sortable  : true,         dataIndex : 'fullName'                              (2)     },     {         header    : 'State',         dataIndex : 'state'     } ]);  var gridView = new Ext.grid.GridView();                     (3) var selModel = new Ext.grid.RowSelectionModel({             (4)     singleSelect : true })  var grid = new Ext.grid.GridPanel({                         (5)     title      : 'Our first grid',     renderTo   : Ext.getBody(),     autoHeight : true,     width      : 250,     store      : store,                                     (6)     view       : gridView,                                      colModel   : colModel,                                            selModel   : selModel }); {1} Creating our ColumnModel{2} Mapping the dataIndexes to the columns themselves{3} Instantiating a new GridView{4} Creating a new single-selection RowSelectionModel{5} Instantiating our Grid{6} Referencing our Store, GridView, ColumnModel and SelectionModel

In the above Listing, we configure all of the supporting classes before constructing the GridPanel itself. The first thing we do is create a reference for a newly instantiated instance of a ColumnModel {1}, for which we pass in an array of configuration objects. Each of these configuration objects are used to instantiate instances of Ext.grid.Column (or any subclasses thereof), which is the smallest managed unit of the ColumnModel. These configuration objects {2} detail the text that is to be populated in the column header and which Record field the column maps to, which is specified by the dataIndex property. This is where we see the direct dependency on the configuration of the Store’s fields and the ColumnModel’s columns. Also, notice that we set sortable to true for the “Full Name” column and not the “State” column. This will enable sorting on just that one column.

We then move on to create an instance of Ext.grid.GridView {3}, which is responsible for managing each individual row for the grid. It binds key event listeners to the DataStore, which it requires to do its job. For instance, when the DataStore performs a load, it fires the “datachanged” event. The GridView listens for that event and will perform a full refresh. Likewise, when a record is updated, the DataStore fires an “update” event, for which the GridView will only update a single row.

Next, we create an instance of Ext.grid.RowSelectionModel {4} and pass a configuration object that instructs the selection model to only allow single selection of rows to occur. There are two things to know this step. The first is that by default, the GridPanel always instantiates an instance of RowSelectionModel and uses it as the default selection model if we do not specify one. But we did create one because by default the RowSelectionModel actually allows for multiple selections. You can elect to use the CellSelectionModel in place of the RowSelectionModel. The CellSelectionModel does not allow for multiple selections of items, however.

After we instantiate our selection model, we move on to configure our GridPanel {5}. GridPanel extends Panel, so all of the Panel-specific configuration items apply. The only difference is you never pass a layout to the GridPanel as it will get ignored. After we set the Panel-specific properties, we set our GridPanel-specific properties {6}. This includes configuring the references for the DataStore, ColumnModel, GridView, and Selection Model. Loading the page will generate a GridPanel that looks like Figure 1.

Figure 1. First GridPanel Rendered: Here is our first grid rendered on screen, demonstrating the single-select configured RowSelectionModel and the sortable “Full Name” column.

As we can see in Figure 1, the data is not in the same order that we specified. This is because before I took the snapshot, I performed a single click on the “Full Name” column, which invoked the click handler for that column. The click handler checks to see if this column is sortable (which it is) and invoked a DataStore sort method call, passing in the data field (“dataIndex”), which is “fullName”. The sort method call then sorts all of the records in the store based on the field that was just passed. It first sorts in an ascending order, then toggles to descending thereafter. A click of the “State” column will result in no sorting because we didn’t specify “sort : true” like we did for the “Full Name” column.

To exercise some of the other features of the ColumnModel, you can drag and drop the columns to reorder them, resize them by dragging the resize handle or click the column menu icon, which appears whenever the mouse hovers over a particular column.

To exercise the Selection Model, simply select a row by clicking on it. Once you’ve done that, you can use the keyboard to navigate rows by pressing the up and down arrow keys. To exercise the multi-select RowSelectionModel, you can simply modify the SelectionModel by removing the “singleSelect: true” property, which defaults to false. Reloading the page will allow you to select many items by using typical Operating System multi-select gestures such as Shift-Click or Ctrl-Click.

Creating our first grid was a cinch. Obviously there is much more to GridPanels than just displaying data and sorting it. Features such as pagination and setting up event handlers for gestures like right-mouse clicks are used frequently, which means that the simple GridPanel we created here can quickly become more complex.

Courtesy of Manning Publications. All rights reserved.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist