Configure sorting and filtering

So, you have a Caplin Grid component, it's working well, but now you want to sort or filter the data... In this tutorial, we'll explain how to use the Grid API to set up parameters for sorting and filtering, how the Grid API is used in Caplin Trader, and also how to configure your own sorting and filtering if you're not using the Caplin Platform to handle your server-side processing.

Sorting

The standard Caplin sorting mechanism handles sorting by sending a request to Caplin Refiner saying something like "Sort the contents of container [xxx], in ascending order, on the column with ID [yyy]". Caplin Refiner then sorts the container appropriately, and sends the results back to Caplin Trader, to be displayed in the grid. Every alteration to the raw data makes Caplin Refiner re-sort them, and immediately updates the grid with the latest version.

Sending the request to the server means that the browser doesn't need to store or sort the container's contents itself. As a container can hold tens of thousands of items, that takes significant loading off the browser.  You initiate sorting by clicking on the header of the column you want to sort by. With each click, the sort process cycles through unsorted, ascending and descending states, with each state triggering a new request to Caplin Refiner. Click-handling is added by configuring a grid decorator class in your gridDefinitions.xml file, as follows:

First of all, add the caplin.grid.decorator.ColumnSortDecorator to the <decorators> tag of the grid that you want to sort:

<grid>
  ...
  <decorators>
	<caplin.column-reset-decorator />
  </decorators>
</grid>

This decorator adds a click-handler, so that when someone clicks on a column, the column is notified that a particular sort order has been requested.  If you are using the Caplin Platform, this will result in a request being sent to Caplin Refiner, which will send back the sorted data. Sorting is only available on one column at a time.

If you want to customise your sorting mechanism, you can write your own grid decorator, or another piece of code, to locate the appropriate GridColumn and call setSortOrder() on it:

function sortGrid(columnIndex)
{
	var gridColumn = this.gridColumnModel.getColumnByIndex(columnIndex);

	// Sort order can be one of SORT_NONE, SORT_ASC, SORT_DESC

	gridColumn.setSortOrder(caplin.grid.GridColumn.SORT_ASC);
};

Filtering

Filtering in Caplin Trader is also done by sending a request to Caplin Refiner to do the actual filtering, but the way the request is triggered is a bit more complicated than for sorting. For filtering, the user needs to be able to enter filter conditions: greater than 5, equal to "ABC", and so forth. To achieve this, renderers are used. When a user types a filter condition into the input box of a renderer, the renderer notifies the grid column that it has had a filter condition added to it. The filter request will then go out to Caplin Refiner, and the filtered response will be displayed in the grid.

Every update to the raw grid data causes Caplin Refiner to re-evaluate the filters, so the grid is immediately notified if any rows subsequently pass or fail the filters. You can also configure filters in the XML, which will be combined with the user-defined filters, although at the moment, it's only possible to apply one user-created filter to each column.

Assuming that you already have a grid configured and displaying correctly, filter configuration works like this:

The first step is to define the renderer in rendererDefinitions.xml. The class used by the renderer to set the filter on the column is caplin.element.handler.grid.CombinedFilterHandler:

<renderer type="novox.fxtrader.grid.filterBox">
  <control className="caplin.control.basic.InputControl">

    <handler className="caplin.element.handler.grid.CombinedFilterHandler">
	<attribute name="filterType" value="WILDCARD" />
    </handler>

  </control>
</renderer>

Next, set the renderer to be used as the headerRenderer of your grid columns in gridDefinitions.xml.  Using the renderer defined above, this example would add a filter-box at the top of each column.

<grid id="novox.fxtrader.grid.FilteringExample" 
	displayName="FilteringExample" 
	displayedColumns="description, bestbid, bestask">

  <columnDefinitions>

    <column id="description" 
	fields="InstrumentDescription" 
	...
	headerRenderer="novox.fxtrader.grid.filterBox" />
	
    <column id="bestbid" 
	cellRenderer="novox.fxtrader.grid.fx-price" 
	fields="BestBid" 
	...
	headerRenderer="novox.fxtrader.grid.filterBox" />

    <column id="bestask" 
	cellRenderer="novox.fxtrader.grid.fx-price" 
	fields="BestAsk" 
	...
	headerRenderer="novox.fxtrader.grid.filterBox" />

  </columnDefinitions>

  <gridRowModel>
    <novox.fxtrader.rttpContainerGridDataProvider 
	container="/CONTAINER/FX/Major" />
  </gridRowModel>

</grid>

If you were to type ">1" in the "Bid" column's filter box, you would see only those currency pairs with a bid price greater than 1.

To define a filter in XML, add <filterExpression> as a child of the <gridRowModel> tag, like this:

<grid>
   ...
   <gridRowModel>
      ...
      <filterExpression name="myFilter">
         <fieldFilter field="STATUS" operator="#" value="COMPLETED" />
      </filterExpression>
   </gridRowModel>
</grid>

The example above applies the "#" operator (which stands for a case-insensitive regular expression) to the "STATUS" column, to filter out anything that does not match the value "COMPLETED".  There are various filter types (as listed below), and each has a short-form version that goes in the operator attribute of the <fieldFilter> tag.  For full details of these filters and their shortened forms, see the Filtering Configuration Reference page.

Additionally, you can configure the renderer to use drop-down boxes instead of input boxes. To do this, just modify the renderer configuration to use caplin.control.basic.DropdownControl instead of caplin.control.basic.InputControl, and define the options available in the drop-down control as follows:

<renderer type="novox.fxtrader.grid.filterBox">
   <control className="caplin.control.basic.DropdownControl">

      <option value="buy">Buy</option>
      <option value="sell">Sell</option>

      <handler className="caplin.element.handler.grid.CombinedFilterHandler">
         <attribute name="filterType" value="WILDCARD" />
      </handler>

   </control>
</renderer>

Adding Booleans to Filters

You can add multiple filters to your XML, combining them with AND and OR gates, to make more complex filter conditions than user-defined filters can achieve. To do this, just use <and> or <or> as XML tags to apply the <fieldFilters> within appropriately.  In the example below, the filter will display rows that have either "COMPLETED" or "CANCELLED" in the "STATUS" column.

<filterExpression name="myFilter">
   <or>
	<fieldFilter field="STATUS" operator="#" value="COMPLETED" />
	<fieldFilter field="STATUS" operator="#" value="CANCELLED" />
   </or>
</filterExpression>

You can also nest Boolean operators, as shown below.  In this example, the filter would return only rows with "GBPUSD" in the "Description" column, AND which has either a Bid value greater than 1.974, OR an Ask value less than 1.976. As you might deduce, the characters < and > are reserved in HTML, the "greater-than" and "less-than" operators are represented by their escape-code equivalents.  You can find out more about the filtering parameters and their operators on the filtering reference page, and more about renderers in the API Reference section.

<filterExpression name="myFilter">
   <and>
	<fieldFilter field="Description" operator="#" value="GBPUSD" />
	<or>
           <fieldFilter field="Bid" operator=">" value="1.974" />
           <fieldFilter field="Ask" operator="<" value="1.976" />
	</or>
   </and>
</filterExpression>
Warning: The name of the <filterExpression> cannot be "columnFilters" as this name is used internally by the grid code.

Customised Filtering

If you want to customise your filtering mechanism, you can write your own code to set filters:

function filterGrid(columnIndex)
{
	var gridColumn = this.gridColumnModel.getColumnByIndex(columnIndex);
	gridColumn.addFilter(caplin.grid.GridColumnFilter.GREATER_THAN, "5");
}

You can use any of the following filter types:

  • LESS_THAN
  • GREATER_THAN
  • EXACT_MATCH
  • PARTIAL_MATCH
  • WILDCARD
  • WILDCARD_CASE_SENSITIVE
  • REGULAR_EXPRESSION
  • REGULAR_EXPRESSION_CASE_INSENSITIVE
  • LESS_THAN_OR_EQUAL
  • GREATER_THAN_OR_EQUAL
  • NUMERIC_MATCH

Further details of the available filtering types and their operators can be found on the Filtering Reference page.

But What If I'm Not Using Caplin Platform?

All of the above assumes that you are using Caplin Platform on the server side, but of course you might not be.  Fortunately, you can use our APIs to "roll your own" sorting and filtering mechanisms, to fit in with your own server-side solution.  This section assumes that you know the basics of providing data to a grid from a webservice, so you might want to have a look at that tutorial if you are unsure about it.

If you're not using Caplin Platform, you can write your own implementation of caplin.grid.GridDataProvider to receive sorting and filtering requests from the user, and send them on to whatever service you are using. The relevant methods for sorting and filtering are:

  • getFilterExpression(sFilterName)
  • getAllFilterExpressions()
  • setFilterExpression(oFilterExpression, sFilterName)
  • clearFilterExpression()
  • getSortRule()
  • setSortRule()
  • clearSortRule()

When your GridDataProvider gets a call to any of these methods, it has to convert the parameters that it receives, into the format that your custom data source understands, and then pass the request along. For instance: if setFilterExpression() is called, your GridDataProvider will need to convert oFilterExpression, possibly also adding it to any other filters previously received.  Responses coming from your back-end systems into your GridDataProvider will need to be converted and passed along to any caplin.grid.GridDataProviderListeners that are registered. Please see the documentation on these classes for further details.