Monday, June 27, 2011

Case insensitive and subsearch with Extjs combobox


Extjs combobox default filters with the start of the combobox item i.e. if one of the combobox item's display value is 'United States' then typing 'Uni' will provide 'United States'. However if you type 'ted' or 'ates' it will not provide 'United States' in the filter list.
Also the combobox default filter is case sensitive i.e. if you type 'uni' then it does not list out 'United States'.

To overcome above restrictions and allow subsearch and case insensitive filter we need to override the combobox (specially the doQuery method).

Following code shows how to override the Extjs combobox to allow any match and case insensitive filter search.



In above example we have overridden the combobox doQuery method to perform any match and also customized the combobox caseSensitive check.
Also we have overridden the onTypeAhead method so as to filter the content based on whatever the substring is provided. If this method is not overridden then on typeAhead filter will be reset to the first character of the matching text.
Once we are done with overriding the combobox, we can use anyMatch and caseSensitive attributes to specify whether we want complete word search or case sensitive search for combobox.

Note : The anyMatch custom attribute is defaulted to false so as to continue with existing combobox functionality. In order to perform any substring match you need to pass true value to combobox config. Whereas caseSensitive is defaulted to false to allow case insensitive check by default. If you want to restrict the case sensitivity then pass the config attribute value as true.

Extjs Store FAQs

While using Extjs store you will come across some common questions like how to reload a store or change its url etc.

Below is the listing of some of the extjs store FAQs and their answers.

Change store url dynamically :
store.proxy.url = newUrl;

Update or add store baseparams
store.baseParams = {
    sort : "name",
    selectedUser : store.getById(userId).data.userName
}
or
store.baseParams.selectedUser :  store.getById("10").data.userName;

Load store from another store
store2.loadData(store1.reader.xmlData);
//to load store with json data use store1.reader.jsonData

Reload the store :
 store.reload(store.lastOptions);

Load store with the value stored in local (javascript) variable :
store.loadData(localVariable); 
// localVariable contains the data to be loaded into the store.

Append data to existing store :
store.loadData(newData, true) //newData contains the data to be appended

Iterate over store records :
store.each(function(currentRecord) {
    alert(currentRecord.id); 
    // alerts record id for each store record
});

Remove a record from the store :
store.remove(recordToRemove);
//recordToRemove is the record object to be removed from store.
//This can also be an array if you want to remove multiple records.
//Use store.removeAll() to remove all the records
//If you know index of the record then use store.removeAt(recordIndex)

Sunday, June 26, 2011

Nested grids using rowexpander

As we all know Extjs is one of the powerful javascript UI frameworks which helps to achieve complex UI functionalities in an easy way. One such complex UI functionality is to show a grid within another grid i.e. having nested grids.

There could be various ways to show nested grids, but using rowexpander it is lot more easy to show nested grids. So today we will see how to show nested grids using rowexpander.

Firstly we will see how to add the rowexpander plugin to a grid. To add the rowexpander to a grid you have to follow two simple steps. 
Step 1 :  You have to add the rowexpander component into the parent grid plugins. So in your parent grid, you have to just add a plugins config and specify the rowexpander over there.
plugins : new Ext.grid.RowExpander(
{
   tpl: new Ext.XTemplate('<div style="overflow:auto;" class="detailData">', '', '</div>')
})
Step 2 : In order to collapse and show the nested grid a column needs to specified which holds the collapse/expand icons for rowexpander. For this you have to add the same rowexpander component into the parent grid columns.

Now that we have added rowexpander component into the parent grid, we will take a look at how to render nested/child grid with rowexpander.
If you have noticed within the rowexpander template a div is specified with the detailData class. This div acts as a place holder for the nested grid. To add the nested grid into this div we listen the 'expand' event of the row expander and render our nested grid into the detailData div body.
expand : function(ex, record, body, rowIndex) {
    if(Ext.DomQuery.select("div.x-panel-bwrap", body).length == 0) {
        var innerRowDiv = Ext.DomQuery.select("div.detailData", body)[0];
        //Prepare nested grid config over here and render it to innerRowDiv variable.
    }
}

Lets gather all the above pieces into an example.For our example we will define both parent and child grid as the Extjs EditorGridPanel.

Here we define the rowexpander component and define the the nested grid to be shown using rowexpander.



Now we define the parent grid and add the rowexpander plugin into it and into its columns.





Try the above example to get the nested grids using Extjs and you will realize how easy it is to show nested grids using rowexpander.

Note : wherever necessary use your custom variables/components like store etc. Also you can use normal Extjs grid for any parent and/or nested grid. If you are using rowexapnder from ux then replace Ext.grid.Rowexpander with Ext.ux.grid.RowExpander

Friday, June 24, 2011

Extjs : override grid column sorting parameter

During UI upgrade for existing web application I came across a scenario where you want a different parameter to be used for sorting than which is mapped to the displayed column. For e.g: You have a grid with a column 'name' and mapped to 'name' field (dataIndex : 'name'). If sorting is enabled on this column then while sorting, 'name' variable is sent as sort variable. But the server expects it to be 'firstname' as sort variable then either you have to change it at the server side or you can achieve the same at client side.
     With Extjs you can achieve this with just couple of changes. 
Firstly within your column config you have to add an extra attribute viz. customSort with a value that has be used to override the sort param for this column. In above case it would be customSort : 'firstname'
Secondly before loading the store data, for the columns specified with customSort config attribute, you need to replace the sort param with the customSort param .

Code :
Thus by overriding the store sort field you can provide your custom sorting for extjs grid columns.
Hope this helps!

Thursday, June 23, 2011

Showing single textfield into multiple columns within extjs grid.

In normal html table it is possible to show a single textfield(or any html component) within multiple columns by using the colspan attribute. With Extjs one way to achieve similar thing is by setting the grid row body. i.e. you can specify the html textfield component within grid row body.
If you don't want to show any column specific data for that particular row then you need to write column renderers and within it you need to return empty value.
e.g: renderer : function(value, p, record, rowIndex) {
    if(rowIndex == 1){ //or custom condition as (record.data.docIndex == 1)
         return '';
    }
}
If you are using an editor grid panel then you need to disable editing row. For this listen beforegridedit event on grid and within it disable the editing of the row in which your textfield is shown.

Below example shows how to achieve this.

Sample example screenshot :


Monday, June 20, 2011

How to show image or checkbox html component in extjs grid column header ?

There can be an use case where an image needs to be shown within certain extjs grid column heder.
Also you might want to show some html components within specific grid column header. This can be achieved by reseting the grid column header after the grid is being rendered. To achieve this you need to add afterrender listener and within it you would need to reset the grid column header. To reset the grid header, you need to get the grid view first and then get the specific column by calling view.getHeaderCell(columnIndex) method and then replace its innerHTML.
Example:
In below example, I am adding the image to a column with id "imageColumn" and a checkbox to a column with id "checkboxColumn". Also I am adding a mandatory flag to identify the fields which are mandatory.
var myGrid = new Ext.grid.GridPanel({
    id: 'demo-grid-header-img-grid',
    layout: 'fit',
    store: gridHeaderStore, // replace it with your custom store.
    cm: gridHeaderColumnModel, // replace it with your custom column model.
    listeners: {
        afterrender: function(_grid){
            var columns = _grid.getColumnModel().config;
            var view = _grid.getView();
            var columnIndex = 0;
            Ext.each(columns, function(column){
                var headerHtml = '<div class="x-grid" + columnIndex + '-hd-inner x-grid' + columnIndex + '-hd-' + column.id + '" unselectable="on" style="">' + column.header;
                // To show mandatory column flag
                headerHtml += '&nbsp;<font color=red>*</font>
                /*column id starting with "imageColumn" text. You can also use column.id == "imageColumn" or your column id*/
                if(column.id.match("^" + "imageColumn")) {
                    headerHtml += '&nbsp;<img src="/images/headerImage.jpg" width=12 height=12>';
                }
                //To add checkbox to specific column header
                else if(column.id.match("^" + "checkboxColumn")){
                    headerHtml += '&nbsp;<input type="checkbox" id="'+column.header+'chk" onclick="javascipt:headerChkHandler()">';
                }
                headerHtml += '</div>';
                //replace the view headerCell innerHTML.
                view.getHeaderCell(columnIndex).innerHTML = headerHtml;
                columnIndex = columnIndex + 1;
            });
        }
    }
});

Note : if enableColumnMove is not disabled for your grid then you would also need to listen 'columnMove' event and repeat the same process there as well.