Planning and coding the grid search

To develop the plugin, we will create a menu where the user can select and deselect the columns of the grid, a text field where the user can write their search query, and a clear button that will help to clear the search query. At first we will develop the required UI fields and then we will add the corresponding functionality to those fields. Now let us start coding:

Ext.define("Examples.plugin.GridSearch", {

  extend : 'Ext.util.Observable',
  alias : 'plugin.gridsearch',

  config : {

    iconCls : 'icon-zoom',
    checkIndexes : "all",
    mode : 'local',
    minChars : 1,
    width : 100,
    searchText : 'Search',
    selectAllText : 'Select all',
    position: 'bottom' ,
    paramNames: {
      fields:'fields'
      ,query:'query'
    }

  },

  init : function(cmp) {

    this.grid = cmp.view.up('gridpanel'),

    if (this.grid.rendered)
      this.onRender();
    else {
      this.grid.on('render', this.onRender, this);
    }

  },
…

You can see that we have defined several configuration options and also the required init function for the plugin. Now let us define the onRender function:

onRender : function() {

  var tb = this.getToolbar();
  this.menu = new Ext.menu.Menu();

  this.field = Ext.create("Ext.form.field.Trigger", {
    width : this.width,
    selectOnFocus : undefined === this.selectOnFocus ? 
             true : this.selectOnFocus,
       triggerCls : 'x-form-clear-trigger', 
        minLength : this.minLength
  });

  tb.add('->', {
    text : this.searchText,
    menu : this.menu,
    iconCls : this.iconCls
  }, this.field);

}

In this function, first we are trying to get the toolbar by calling the getToolbar function as we need to render our plugin UI on the toolbar. Then we are creating the menu field, which will hold the column selections, and then the search field. After this, we will add the menu field and the search field to that toolbar. Now let us define the getToolbar function:

getToolbar: function(){

  var me = this,
  dockedItems = this.grid.getDockedItems(),
  toolbar = null,
  hasToolbar = false;

  if(dockedItems.length>0){
    Ext.each(dockedItems, function(item){
      if(item.xtype ==='toolbar' && item.dock == me.position){
        hasToolbar = true;
        toolbar = item;
        return false;
      }
    });
  }

  if(!hasToolbar){
    toolbar = this.grid.addDocked({
      xtype: 'toolbar',
      dock: this.position
    })[0];
  }

  return toolbar;

}

In this function we are looking for a toolbar item, which is docked at the location defined in the position configuration option. We will render our plugin UI on this returned toolbar.

Now let us use this plugin within a grid and the output of the plugin should look like the following screenshot:

Planning and coding the grid search

So, now we have our plugin looking exactly the same as the requirement. Now let us start adding functionality. First let us modify the onRender function of our plugin code:

this.field = Ext.create("Ext.form.field.Trigger", {
  width : this.width,
  selectOnFocus : undefined === this.selectOnFocus ?
           true : this.selectOnFocus,
     triggerCls : 'x-form-clear-trigger',
 onTriggerClick : Ext.bind(this.onTriggerClear, this),
      minLength : this.minLength
});

You can see that we have provided the onTriggerClear handler for the onTriggerClick event to clear the search. We need to add and handle some keyboard events: pressing the Enter key will trigger searching and pressing the Esc key will trigger clearing the search. So, we need to add the following code after defining the trigger field:

this.field.on('render', function() {

  if (this.minChars) {
    this.field.el.on({
      scope : this,
      buffer : 300,
      keyup : this.onKeyUp
    });
  }

  var map = new Ext.KeyMap(this.field.el, [{
    key : Ext.EventObject.ENTER,
    scope : this,
    fn : this.onTriggerSearch
  }, {
    key : Ext.EventObject.ESC,
    scope : this,
    fn : this.onTriggerClear
  }]);
  map.stopEvent = true;
}, this, {
  single : true
});

Now, we need to prepare the menu to load the column names and we will call the initMenu function to do that. And that's all we needed to do within the onRender function.

Now let us define the onKeyUp handler:

onKeyUp : function(e) {

  if (e.isNavKeyPress()) {
    return;
  }

  var length = this.field.getValue().toString().length;
  if (0 === length || this.minChars <= length) {
    this.onTriggerSearch();
  }

}

Let us go ahead with defining the initMenu function:

initMenu : function() {

  var menu = this.menu;
  menu.removeAll();

  menu.add(new Ext.menu.CheckItem({
    text : this.selectAllText,
    checked : !(this.checkIndexes instanceof Array),
    hideOnClick : false,
    handler : function(item) {
      var checked = item.checked;
      menu.items.each(function(i) {
        if (item !== i && i.setChecked && !i.disabled) {
          i.setChecked(checked);
        }
      });
    }
  }), '-'),

  var cm = this.grid.headerCt.items.items;

  var group = undefined;
  Ext.each(cm, function(item) {
    var config = item.initialConfig;
    var disable = false;

    if (config.header && config.dataIndex) {
      Ext.each(this.disableIndexes, function(item) {
        disable = disable ? disable : 
          item === config.dataIndex;
      });
      if (!disable) {
        menu.add(new Ext.menu.CheckItem({
          text : config.header,
          hideOnClick : false,
          group : group,
          checked : 'all' === this.checkIndexes,
          dataIndex : config.dataIndex
        }));
      }
    }
  }, this);

  if (this.checkIndexes instanceof Array) {
    Ext.each(this.checkIndexes, function(di) {
      var item = menu.items.find(function(itm) {
        return itm.dataIndex === di;
      });
      if (item) {
        item.setChecked(true, true);
      }
    }, this);
  }

}

You can see how we are preparing the menu for selecting and deselecting the columns in the preceding initMenu function. Now let us define the onTriggerClear function, which is responsible for clearing the search query:

onTriggerClear : function() {

  if (this.field.getValue()) {
    this.field.reset();
    this.field.focus();
    this.onTriggerSearch();
  }

}

Next we define the onTriggerSearch function:

onTriggerSearch : function() {

  if (!this.field.isValid()) {
    return;
  }
  var val = this.field.getValue(),
    store = this.grid.store,
    proxy = store.getProxy();
…

We need to check against the value set for the mode configuration option and need to provide separate logic if the value is set to 'local' or if the proxy of the store is a server proxy. Now we need to add the following code within the onTriggerSearch function when the mode is set with 'local':

if ('local' === this.mode) {
  store.clearFilter();
  if (val) {
    store.filterBy(function(r) {
      var retval = false;
      this.menu.items.each(function(item) {
        if (!item.dataIndex || !item.checked || retval) {
          return;
        }

        var rv = r.get(item.dataIndex), rv = rv instanceof Date ?
        Ext.Date.format(rv, this.getDateFormat(item)) : rv;
        var re = new RegExp(Ext.String.escape(val), 'gi'),
        retval = re.test(rv);
      }, this);
      if (retval) {
        return true;
      }
      return retval;
    }, this);
  }
}

And if the value is not set to local, we need to check whether the proxy is a server proxy or not. And here is the code that we need to add within the onTriggerSearch function after the if ('local' === this.mode) block:

else if(proxy instanceof Ext.data.proxy.Server) {

  if(store.lastOptions && store.lastOptions.params) {
    store.lastOptions.params[store.paramNames.start] = 0;
  } 

  var fields = [];
  this.menu.items.each(function(item) {
    if(item.checked && item.dataIndex) {
      fields.push(item.dataIndex);
    }
  });

  delete(proxy.extraParams[this.paramNames.fields]);
  delete(proxy.extraParams[this.paramNames.query]);
  if (store.lastOptions && store.lastOptions.params) {
    delete(proxy.lastOptions.params[this.paramNames.fields]);
    delete(proxy.lastOptions.params[this.paramNames.query]);
  }
  if(fields.length) {
    proxy.extraParams[this.paramNames.fields] = Ext.encode(fields);
    proxy.extraParams[this.paramNames.query] = val;
  }

  store.load();
}

Now we define the getDateFormat function:

getDateFormat : function(menuItem) {

  var columnNames = Ext.Array.pluck(this.grid.columns, 'dataIndex'),
  columnIndex = Ext.Array.indexOf(columnNames, menuItem.dataIndex),
  format = this.grid.columns[columnIndex].format;

  return this.dateFormat || format;
}

Following is the screenshot of our working plugin:

Planning and coding the grid search

You can see that our plugin filters data according to the search query.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset