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.