2. Adding simple Criteria, sort, and data paging with Ruby on Rails

Description

This example extends sample 1 (Handling a fetch request from RestDataSource using Ruby on Rails), but will have support for pagination, filtering and sorting built onto it.

Changes to client side code

Firstly, Change the DataSource so, instead of using the URL parameters for sending data to the back-end (filter, etc), send parameters as a JSON payload to the POST method. Additionally, configure the ListGrid to let the back-end do the pagination and filtering.

For this, remove the 'dataFetchMode:"local"' attribute on the grid. This will cause the grid to send criteria and pagination information to the back-end:

isc.ListGrid.create({
    ID: "suppyItem",
    width: 700,
    height: 224, 
    alternateRecordStyles: true,
    dataSource: suppyItem,
    showFilterEditor: true,
    autoFetchData: true,
    dataPageSize: 20
});

Please note:, Grid filters have also been enabled with showFilterEditor:true. This allows for easier testing later. Also note that the dataPageSize has been set to 20, to show pagination working.

Additionally, change the DataSource definition to use POST instead of the default GET HTTP method used by normal fetch operations. This will post the JSON as a payload to the server instead of passing it the variables in a URL. To do this, add an operationBinding definition to the DataSource. The DataSource should now look like this:

isc.RestDataSource.create({
    "ID": "suppyItem",
    "fields":[
        {"name":"itemID", "type":"sequence", "hidden":"true", "primaryKey":"true"},
        {"name":"itemName", "type":"text", "title":"Item", "length":"128", "required":"true"},
        {"name":"SKU", "type":"text", "title":"SKU", "length":"10", "required":"true"},
        {"name":"description", "type":"text", "title":"Description", "length":"2000"},
        {"name":"category", "type":"text", "title":"Category", "length":"128", "required":"true" },
        {"name":"units", "type":"enum", "title":"Units", "length":"5",
            "valueMap":["Roll", "Ea", "Pkt", "Set", "Tube", "Pad", "Ream", "Tin", "Bag", "Ctn", "Box"]
        },
        {"name":"unitCost", "type":"float", "title":"Unit Cost", "required":"true",
            "validators":[
                {"type":"floatRange", "min":"0", "errorMessage":"Please enter a valid (positive) cost"},
                {"type":"floatPrecision", "precision":"2", "errorMessage":"The maximum allowed precision is 2"}
            ]
        },
 
        {"name":"inStock", "type":"boolean", "title":"In Stock"},
        {"name":"nextShipment", "type":"date", "title":"Next Shipment"}
    ], 	
    "dataFormat":"json",
    "operationBindings": [
                { "operationType": "fetch", "dataProtocol": "postMessage", "dataURL": "/smartclient/data" },                
            ] 
});

Changes on the back-end

If the sample,was run at this stage the payload sent to the server will look similar to this:

{
    "dataSource":"suppyItem",
    "operationType":"fetch",
    "startRow":0,
    "endRow":20,
    "textMatchStyle":"substring",
    "componentId":"suppyItem",
    "data":{
        "units":"Pkt",
        "unitCost":"a"
        ...
    },
    "oldValues":null
}

The "smartclient" gem supports the DSRequest class to define the object which is created by de-serializing from JSON and which mimics the previous JSON. 

You only need to include the DSRequest.rb after the "smartclient" gem was installed.

smartclient_controller.rb
require 'DSResponse'
require 'DSRequest'
class SmartclientController < ApplicationController         
    def index
      
    end
    
    def data      
      # set the request parameters
      request = DSRequest.new(params)
      unless request.data.empty?                      
          @supplyItems = Supplyitem.filter(request)        
          supplyitems_count =  Supplyitem.filter(request).count
      else
          # get all supplyitems from the database
          @supplyItems = Supplyitem.find(:all)                    
          supplyitems_count = Supplyitem.count
      end 
     
      # get the count of the supplyitems
      
      endRow = (supplyitems_count > 0)?supplyitems_count -1 : supplyitems_count 
      response = DSResponse.new
      response.data = @supplyItems
      response.startRow = 0
      response.endRow = endRow
      response.status = 0
      response.totalRow = supplyitems_count      
            
      @result = { :response => response }
      
      render json: @result
    end 
end 

And then, you need to define the method for filtering in model.

model\supplyitem.rb
def self.filter(request)    
    param = Array.new    
    condition = '' 
    request.data.each do |key, value|
        condition += "#{key} LIKE ? AND "
        param << "%" + value + "%" 
    end
    q = condition[0, condition.rindex('AND ')]
    temp = Array.new
    temp << q    
    temp.concat(param)
    where(temp)
    order = ''
    # sort by
    unless request.sortBy.nil?
      request.sortBy.each do |idx|
         if idx.index('-') === nil 
              order = idx.to_s + " ASC"
         else
              order = idx.to_s + " DESC"    
         end
      end  
    end    
   # return the result
    
   if order == nil
      where(temp)
   else
     where(temp).order(order)
   end  
  end 

Although the code looks a little more complicated than in the previous sample, it is actually very straight forward:

1. Create a stream to read the JSON payload as a string,The string is then used to de-serialize the DSRequest object from.

2. Using this object, create a query for getting the records, and another for getting the total count of records. Also, based on the request object, set up the sorting criteria of the records to send back.

3. Create a DSResponse object, run the queries and fill the fields of the response object and send it back to the front-end, as in the previous sample.

The complete code for this sample project can be downloaded from github.