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.
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.
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.