A Client Update Part II

In the previous article, I described the setting up of the page for the extjs data grid. Now it's time to get into working with the ext libraries.

First, I downloaded ext and put it in my /assets/js/ folder.

Using the Data Grid Object

I'll show the entire .js file to begin with, then go over it part by part.

Ext.onReady(function(){

    Ext.BLANK_IMAGE_URL = 'assets/js/ext2/resources/images/default/s.gif';

    var store = new Ext.data.Store({
        proxy: new Ext.data.HttpProxy({
            url: 'assets/snippets/database/grid-paging-data.php'
        }),

        reader: new Ext.data.JsonReader({
            root: 'results',
            totalProperty: 'total',
            id: 'term',
            fields: [
                {name: 'tb_id', type: 'int'}, 
                'german', 
                'english', 
                'italian', 
                'french'
            ]
        }),

        remoteSort: true
    });
 
    store.setDefaultSort('english', 'asc');
    
    var cm = new Ext.grid.ColumnModel([{
           header: "Id",
           dataIndex: 'tb_id',
           width: 65
        },
        {
           header: "German",
           dataIndex: 'german',
           width: 225
        },
        {
           header: "English",
           dataIndex: 'english',
           width: 225
        },
        {
           header: "Italian",
           dataIndex: 'italian',
           width: 225
        },
        {
           header: "French",
           dataIndex: 'french',
           width: 225
        }
    ]);

    cm.defaultSortable = true;

    var grid = new Ext.grid.GridPanel({
        el:'topic-grid',
        width:985,
        height:500,
        title:'View Termbase',
        store: store,
        cm: cm,
        trackMouseOver:false,
        loadMask: true,
        enableHdMenu: false,
        bbar: new Ext.PagingToolbar({
            pageSize: 25,
            store: store,
            displayInfo: true,
            displayMsg: 'Displaying rows {0} - {1} of {2}',
            emptyMsg: "No data to display",
            items:[
                '-', 
                {text: 'Search', handler: 
                    function(){
                        grid.store.load({params:{start: 0, limit: 25}});                    
                    }
                },
                {xtype: 'field', id: 'filter', name: 'filter', autoCreate: {tag: "input", type: "text", size: "20", autocomplete: "off"}}            
            ]
        })
    });
    
    grid.render();
    
    store.on('beforeload', function() {
        store.baseParams = {
            client:document.getElementById('clientName').innerHTML,
            filter:document.getElementById('filter').value
        };
    }); 

    store.load({params:{start:0, limit:25}});

});

To start with, you can see that the whole thing is wrapped in one big function.

Ext.onReady(function(){

This means that as soon as the page is loaded into the browser, the script will run. In this case, the data grid is immediately loaded with the first set of paged data.

Next you'll see a line specifying the URL (relative in this case) to a 1-pixel transparent image that the grid needs for its proper layout. The default URL for this image is the extjs home site, so you'll want to make sure to set this to wherever you put your library files.

    Ext.BLANK_IMAGE_URL = 'assets/js/ext2/resources/images/default/s.gif';

The Data Store Object

This object defines the type of data you're using. It establishes where the data is coming from, and in what form it will be coming.

    var store = new Ext.data.Store({
    ...
    });

First it takes a proxy object; how and where is it going to fetch its data. In my case, since my data resides on the same server as the script, I'm using the HttpProxy.

         proxy: new Ext.data.HttpProxy({
            url: 'assets/snippets/database/grid-paging-data.php'
        }),

GOTCHA: Take note of the comma at the end. This is the first in a list of parameters for the data store object, so it needs a comma, not a semicolon. Be very careful not to have a comma after the last parameter, or IE will fail to display anything.

Next we need to specify a data reader. I'm formatting my data as json in the back-end, so I'll use the JsonReader object.

        reader: new Ext.data.JsonReader({
            root: 'results',
            totalProperty: 'total',
            id: 'term',
            fields: [
                {name: 'tb_id', type: 'int'}, 
                'german', 
                'english', 
                'italian', 
                'french'
            ]
        }),

This tells ext that I'm returning several pieces of data. To begin with, the name of the array containing the results of the query is "results". Another value being returned is the number of result rows being returned, this is the totalProperty and named "total". The element ID (for use in CSS or DOM traversal) should be "term". The next section lays out the structure of the data being returned. In my case, I was displaying words in up to four different languages. So I'm returning the row ID, and each of the four language fields.

Finally, I specified that the result set should be sorted at the back-end.

        remoteSort: true

Notice, no comma! This is the last parameter to my data store object.

Now that the data store object has been defined, I can use it.

    store.setDefaultSort('english', 'asc');

This sets the default sort to the English field. So when the grid is first displayed, the first and subsequent pages of data will be sorted by English.

The Grid Column Model

Ok, we've got the data in our data store, now what are we going to do with it? I want to display it in a grid, so I'll configure a grid ColumnModel object to tell my grid how to lay out my data.

    var cm = new Ext.grid.ColumnModel([{
           header: "Id",
           dataIndex: 'tb_id',
           width: 65
        },
        {
           header: "German",
           dataIndex: 'german',
           width: 225
        },
        {
           header: "English",
           dataIndex: 'english',
           width: 225
        },
        {
           header: "Italian",
           dataIndex: 'italian',
           width: 225
        },
        {
           header: "French",
           dataIndex: 'french',
           width: 225
        }
    ]);

This should be pretty self-explanatory; it just describes the columns I want in the grid.

And since I've defined my column model object, I can use it to state that I want these columns all sortable by default. You can specify if a column is sortable in its description in the column model object if you prefer.

    cm.defaultSortable = true;

The Grid Object

Now I define my grid object.

    var grid = new Ext.grid.GridPanel({
        el:'topic-grid',
        width:985,
        height:500,
        title:'View Termbase',
        store: store,
        cm: cm,
        trackMouseOver:false,
        loadMask: true,
        enableHdMenu: false,
        bbar: new Ext.PagingToolbar({
            pageSize: 25,
            store: store,
            displayInfo: true,
            displayMsg: 'Displaying rows {0} - {1} of {2}',
            emptyMsg: "No data to display",
            items:[
                '-', 
                {text: 'Search', handler: 
                    function(){
                        grid.store.load({params:{start: 0, limit: 25}});                    
                    }
                },
                {xtype: 'field', id: 'filter', name: 'filter', autoCreate: {tag: "input", type: "text", size: "20", autocomplete: "off"}}            
            ]
        })
    });

Lots of parameters! We'll take them one at a time.

el - the element to put the grid into. If you read the previous article, you'll recall that in my document I put this: <div id="grid-paging" style="margin:20px auto; border:1px solid #99bbe8; overflow:hidden; width:980px; height:450px;"></div>

So the el parameter "grid-paging" tells ext to put the grid in that div container.

width, height, title - specify the width and height of the grid, and what title to put in the top title bar of the grid.

store - the name of the data store, which we set when we created our data store object.

cm - the column model object to use.

trackMouseOver - This will cause the row the mouse is hovered over to be highlighted. I don't particularly want that for this grid, so I set it to false.

loadMask - this enables or disables the optional "Loading..." message and translucent grey screen overlay while loading data. It's a nice "Web 20" kind of touch.

enableHdMenu - enables or disables a drop-down menu in the header for each column. I don't want one, so I put "false".

bbar - this is for the bottom bar. It in itself is an object, and in this case I wanted a paging grid.

The Paging Bottom Bar

pageSize - how many pages to load each time.

store - again, the name of our data store object.

displayInfo - enables or disables paging information on the right side of the bar.

displayMsg - the text to display

emptyMsg - text to display if no records were found.

items - any custom items you want to add to the bar. I wanted to add a simple search field.

Adding a Search Field

I just wanted a simple search field, so I added it to the bottom bar. First, I added a hyphen to separate the prev-next paging buttons from the search field. Then I added a search button, with a function to reload the grid when it's clicked. The default item is a button, so I didn't need to specify what kind of element to add here.

 

{text: 'Search', handler: 
    function(){
        grid.store.load({params:{start: 0, limit: 25}});                    
    }
},

GOTCHA The bottom bar object has not yet been instantiated, so even though we did specify a store object, we can't access the bbar.store object at this point. We have to use the grid.store object here.

And finally, I added a field for the user to enter his search word or phrase into. This is not a very sophisticated search, but it is sufficient for my needs.

It took me several hours of searching through the ext documentation, and trial-and-error, before I finally got it to work properly. Let's take a closer look at the search field.

{xtype: 'field', id: 'filter', name: 'filter', 
autoCreate: {tag: "input", type: "text", size: "20", autocomplete: "off"}}

The "xtype" tells ext what type of element this is; it's a field. Then its id, which is important, as we'll see later. Next is the field's name. Then I specify how it is to be created: its HTML tag is "input", its type is "text", its size is "20" and autocomplete is "off", so ext will create this:

<input type="text" name="filter" id="filter" size="20" autocomplete="off">

Finally, now that the grid and all of its parts have been defined, we tell ext to do it.

    grid.render();

At this point, even with no data, a grid will show on the page. An empty grid, of course.

Fetching the Data

Since I want the data filtered and sorted, I have to tell it to pass these POST values on to the back-end script.

    store.on('beforeload', function() {
        store.baseParams = {
            client:document.getElementById('clientName').innerHTML,
            filter:document.getElementById('filter').value
        };
    }); 

Now, every time the data store gets loaded into the grid, these values get sent off along with the AJAX request. The 'client' POST value is taken from the form at the top of the page. The 'filter' value comes from the field we added to the grid's bottom bar. Now you see why the ID we gave that field was important.

And at last, the initial data load is requested (remember, this is all inside of the onReady function, so it will be fired off as soon as the page is loaded).

    store.load({params:{start:0, limit:25}});

Since this is a paging grid, we specify the initial starting point and limit for the records to select. The grid automatically keeps track so subsequent requests will select the proper set of records. But the actual database query is the job of the back-end script, and we'll go over that in the next article.

Continue...