Sensible remoteSort for Ext JS

If you’re managing complex model relationships like I am on the server side, you might find you need to sort on multiple columns to maintain correct ordering or perhaps because Ruby on Rails uses SELECT DISTINCT ON internally which exhibits unpredictable results when combined with PostgreSQL and LIMIT.

Presently, Ext JS will simply send the name of the Ext.data.Record a column is associated with via dataIndex of the columnModel to the server for sorting. If you need to sort on more than one column, you’re either forced to overload the name attribute or add a new attribute to Ext.data.Field. I went the latter route, overriding two functions in Ext.data.Store: The original code doesn’t work with ExtJS 3.0 and above, so I am doing something more sensible now:

// Attempt to override sorting so remoteSortKey can be used instead.
Ext.apply(Ext.data.Field.prototype, {
	remoteSortKey: null
});
...

On the backend, a method then handles the building of the sort portion of the SQL query.

  def sort_order(default="#{model_from_controller.to_s.pluralize.underscore}.id")
 
    columns = (params[:c] || default.to_s).split(',').select do |pair|
      pair.split(/\./).length == 2
    end.collect do |pair|
      pair.split(/\./)
    end.collect do |model,column|
      {:column => column.gsub(/([^a-z_]+)/,'')}.merge(table_name_from_model(model))
    end
 
    return "#{default} ASC" if columns.blank?
 
    sorts = []
    sort = " #{params[:d] == 'down' or params[:d] == 'DESC' ? 'DESC' : 'ASC'}"
    columns.each do |column|
      if column[:model].columns_hash.include? column[:column]
        sorts << case column[:model].columns_hash[column[:column]].type
        when :string
        # Case insensitive for sorting.
          "UPPER(#{column[:table]}.#{column[:column]})" << sort
        else
          "#{column[:table]}.#{column[:column]}" << sort
        end
      else
        sorts << "#{default}" << sort
      end
    end
 
    sorts.join(',')
  end

Thereafter, the following is received server side:

Parameters: {"search"=>{"last"=>""}, "c"=>"people.last,people.first", "d"=>"ASC",
  "action"=>"index", "start"=>"0", "controller"=>"people", "page"=>"1",
  "_dc"=>"1229481474614", "limit"=>"10"}

An Ext.data.Record.

m.Person = Ext.extend(m.Base, {
  root:'people',
  baseParams:{'search[last]':''},
  record:Ext.data.Record.create([
    {name:'first', type:'string'},
    {name:'last', type:'string'},
    {name:'name', type:'string', mapping:'name', sortKey:'people.last,people.first'}]
});

Simply ensure the overrides to Ext JS are loaded after you’ve loaded all Ext JS, but before you load any of your own JavaScript code.

Post a Comment

Your email is never shared. Required fields are marked *

*
*