Zafu: KendoUI JSP autocomplete revisited


In Zafu: KendoUI JSP taglib + Couchbase (3) I have explained how to implement an autocomplete for accessing the name of the beers of Couchbase sample-beer database. That time I have used a pretty complex structure for mapping KendoUI filter.

But if the only thing that I need is sending whatever the user has typed so far, might not be worthwhile using such complex structure. If that’s your case, this is what you have to do.

Redefine parameterMap

In the previous post I have used the following function for generating the parameters to send to the server.

function kendoJson(d, t) {
    return "param=" + JSON.stringify(d);
}

Now, I have redefined it as:

function kendoJson(d, t) {
    return "name=" + d.filter.filters[0].value + "&limit=" + d.take;
}

I.e., only send the data typed so far as a parameter called name and limit as the number of results that is the value defined in kendo:dataSource JSP tag.

Get the parameters in the server

In the server now the parameters are much easier to retrieve since they arrive as two different parameters with the names name and limit.

String name = request.getParameter("name");
int limit = 10;
System.out.println("Limit:" + request.getParameter("limit"));
if (request.getParameter("limit") != null) {
    try {
        limit = Integer.parseInt(request.getParameter("limit"));
    } catch (Exception e) {
        e.printStackTrace();
    }
}

And then invoke the query as we explained in the last post:

Query query = new Query();
query.setIncludeDocs(false);
query.setStale(Stale.FALSE);
query.setDescending(false);
query.setReduce(false);
query.setSkip(0);
query.setLimit(limit);
query.setRangeStart(name)
ViewResponse result = client.query(view, query);
Iterator itr = result.iterator();

Zafu: KendoUI JSP taglib + Couchbase (3)


In the previous two previous posts of this serie, I’ve shown how to use KendoUI Grid with server-side paging and client paging, using the all new KendoUI JSP wrapper. It’s time to take a look into another of KendoUI widgets: autocomplete, and use it for creating an input that suggested the name of one of the beer provided in Couchbase sample-beer database.

KendoUI autocomplete using JSP taglib

Lets start showing all the JSP code for the dissecting it:

<kendo:autoComplete name="beer" dataTextField="name" ignoreCase="false">
    <kendo:dataSource pageSize="10" serverPaging="true" serverFiltering="true">
        <kendo:dataSource-transport parameterMap="kendoJson">
            <kendo:dataSource-transport-read url="/BeerNames"/>
        </kendo:dataSource-transport>
        <kendo:dataSource-schema type="json" data="data"/>
    </kendo:dataSource>
</kendo:autoComplete>

In kendo:autoComplete I’ve used two attributes:

  1. dataTextField value will be sent to the server as parameter and we use it for indicating the field of our document used for filtering.
  2. ignoreCase allow us to tell the server if the filtering should or not differentiate lowercase from uppercase characters.

In kendo:dataSource I used three attributes that allow me to control the filtering of the data. What I saying is:

  1. pageSize: I only want to retrieve a maximum of 10 records (name of beers).
  2. serverPaging: Paging is going to be done in the server, meaning that the server should pick that 10 and send to the client (to KendoUI) a maximum of 10 records.
  3. serverFiltering: the filtering (I mean, the server should choose those records that start with the typed characters and send me the next 10).

In kendo:dataSource-transport I have chosen to encode the parameters by myself to overcame the problem found with KendoUI JSP wrapper and the way that it sends the arguments to the server by default (see First “problem” in KendoUI wrapper in my post Zafu: KendoUI JSP taglib + Couchbase (2)).

Finally, in kendo:dataSource-schema I let KendoUI know that data is going to be received in JSON format and  in an object element called data. So, it is going to be something like this:

{"data":[
    { "name":"A. LeCoq Imperial Extra Double Stout 1999" },
    { "name":"A. LeCoq Imperial Extra Double Stout 2000" },
    { "name":"Abana Amber Ale" },
    { "name":"Abbaye de Floreffe Double" },
    { "name":"Abbaye de Saint-Martin Blonde" },
    { "name":"Abbaye de Saint-Martin Brune" },
    { "name":"Abbaye de Saint-Martin Cuvée de Noel" },
    { "name":"Abbaye de Saint-Martin Triple" },
    { "name":"Abbaye de St Amand" },
    { "name":"Abbey 8" }
]}

Where we see that the array of results is called data, beer names is returned as the value of the attribute name and we get 10 results.

Parameters sent to the server

But what is what I am going to receive into the server (the servlet)? It is going to be a parameter called <em>param</em> which value is something like:

{
    "take":    10,
    "skip":    0,
    "page":    1,
    "pageSize":10,
    "filter":  {
        "logic":  "and",
        "filters":[
            {
                "value":     "a",
                "operator":  "startswith",
                "field":     "name",
                "ignoreCase":false
            }
        ]
    }
}

That says that the server should take 10 results, skip the 0 first and the filtering condition are values that startwith an “a” in the column name and should not ignore case. Since there is only one condition the and is not important (these is used when filtering by several condition where we might choose that all condition must be fulfill -using and– or enough if one condition is met -using or-.

Using the parameters in the server

For a question of convenience I did create a Java class for accessing the conditions of the search. This class are something like this:

public class RequestParameters {
    public int take;
    public int skip;
    public int page;
    public int pageSize;
    public Filter filter = new Filter();
}

public class Filter {
    public String                     logic;
    public ArrayList filters = new ArrayList();
}

public class FilterCondition {
    public String  value;
    public String  operator;
    public String  field;
    public Boolean ignoreCase;
}

Where I divided the parameter sent in three classes: RequestParametersFilter and FilterCondition.

When the servlet receives the paramater, I fill this structure using:

Gson gson = new Gson();
RequestParameters params = gson.fromJson(request.getParameter("param"),
                                         RequestParameters.class);

And Couchbase query is filled as:

Filter filter = params.filter;
FilterCondition cond = filter.filters.get(0);
Query query = new Query();
query.setIncludeDocs(false);
query.setStale(Stale.FALSE);
query.setDescending(false);
query.setReduce(false);
query.setSkip(params.skip);
query.setLimit(params.take);
query.setRangeStart(cond.value)
ViewResponse result = client.query(view, query);
Iterator itr = result.iterator();

And I fill the response using:

PrintWriter out = response.getWriter();
out.println("{\"data\":[");

StringBuilder buf = new StringBuilder();
for (Object result : results) {
    buf.append(",{ \"name\":\"").append(((ViewRow) result).getKey()).append("\"}");
}
if (buf.length() > 0) {
    out.print(buf.substring(1));
}
System.out.println("buffer:" + buf);
out.println("]}");