Spring MVC + DataTables 1.10 Parameters binding
Asked Answered
S

4

9

I'm trying to do a controller that do the Server Side for DataTables.

@RequestMapping(value="/grid", method={RequestMethod.GET}, produces= MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public DataTablesResponse<MyObject> grid(DataTablesRequest dt) {
        return service.getListOfMyObjects();
    }

DataTablesRequest.class:

public class DataTablesRequest {

    private int draw;
    private int start;
    private int length;

    private Search search;

    private List<Order> order;

    private List<Column> columns;

   //... Getters and setters
}

Order.class:

public class Order {
    private String column;
    private String dir;
//...getters and setters
}

Column.class

public class Column {

    private String data;
    private String name;
    private boolean searchable;
    private boolean orderable;
    private Search search;
//...Getters and setters
}

Search.class:

public class Search {
    private String value;
    private boolean regex;
//...getters and setters
}

The problem is that DataTables sends parameters like: column[0][name] and SpringMVC is expecting something like column[0].name.

Is there any way to fix that? How can i bind the parameters of the DataTables to an object?

Septicemia answered 28/1, 2015 at 16:50 Comment(2)
I tried to integrate some parts of datatables v1.10.+ into Spring 4, I came up with this github.com/joaoevangelista/spring-data-datatables-integration, also have a look at dandelion.github.io/datatables/features/ajax/…. It's currently not designed for v1.10.+ put might be helpfulCondescendence
Nice, but this code is missing the problematic part of my issue: The ordering and Columns informations =/Septicemia
E
7

Kind of a late answer, but maybe this would help someone in the future:

In datatables settings, we need to tell jQuery to not process data and to send JSON:

ajax: {
     url: "/some/url.json"),
     type: "POST",
     data: function (data) {
         return JSON.stringify(data);
     },
     dataType: "json",
     processData: false,
     contentType: 'application/json;charset=UTF-8'
},

on the Spring side:

@RequestMapping(value = "/some/url")
@ResponseBody
public DataTablesResponse<Element> list(@RequestBody final DataTablesRequest dataTablesRequest) {
    // query the DB to get the number of elements (without filtering)
    // and the list of elements (filtered and/or paginated)
    return new DataTablesResponse(numberOfElements, notifications.getTotalElements(), dataTablesRequest.getDraw(), "", listOfElements);
}

With the following for DataTablesRequest etc (@Data is from lombok, javadoc comments are from the official datatables doc.):

@Data
public class DataTablesRequest {
    /**
     * Draw counter. This is used by DataTables to ensure that the Ajax returns from server-side processing requests are drawn in sequence by DataTables
     * (Ajax requests are asynchronous and thus can return out of sequence). This is used as part of the draw return parameter (see below).
     */
    private int draw;

    /**
     * Paging first record indicator. This is the start point in the current data set (0 index based - i.e. 0 is the first record).
     */
    private int start;

    /**
     * Number of records that the table can display in the current draw. It is expected that the number of records returned will be equal to this number, unless
     * the server has fewer records to return. Note that this can be -1 to indicate that all records should be returned (although that negates any benefits of
     * server-side processing!)
     */
    private int length;

    /**
     * @see Search
     */
    private Search search;

    /**
     * @see Order
     */
    @JsonProperty("order")
    private List<Order> orders;

    /**
     * @see Column
     */
    private List<Column> columns;
}

@Data
private static class Search {
    /**
     * Global search value. To be applied to all columns which have searchable as true.
     */
    private String value;

    /**
     * <code>true</code> if the global filter should be treated as a regular expression for advanced searching, false otherwise. Note that normally server-side
     * processing scripts will not perform regular expression searching for performance reasons on large data sets, but it is technically possible and at the
     * discretion of your script.
     */
    private boolean regex;
}

@Data
private static class Order {
    /**
     * Column to which ordering should be applied. This is an index reference to the columns array of information that is also submitted to the server.
     */
    private int column;

    /**
     * Ordering direction for this column. It will be <code>asc</code> or <code>desc</code> to indicate ascending ordering or descending ordering,
     * respectively.
     */
    private String dir;
}

@Data
private static class Column {
    /**
     * Column's data source, as defined by columns.data.
     */
    private String data;

    /**
     * Column's name, as defined by columns.name.
     */
    private String name;

    /**
     * Flag to indicate if this column is searchable (true) or not (false). This is controlled by columns.searchable.
     */
    private boolean searchable;


    /**
     * Flag to indicate if this column is orderable (true) or not (false). This is controlled by columns.orderable.
     */
    private boolean orderable;

    /**
     * Search value to apply to this specific column.
     */
    private Search search;

    /**
     * Flag to indicate if the search term for this column should be treated as regular expression (true) or not (false). As with global search, normally
     * server-side processing scripts will not perform regular expression searching for performance reasons on large data sets, but it is technically possible
     * and at the discretion of your script.
     */
    private boolean regex;
}

@Data
public class DataTablesResponse<T> {
    /**
     * The draw counter that this object is a response to - from the draw parameter sent as part of the data request. Note that it is strongly recommended for
     * security reasons that you cast this parameter to an integer, rather than simply echoing back to the client what it sent in the draw parameter, in order
     * to prevent Cross Site Scripting (XSS) attacks.
     */
    private int draw;

    /**
     * Total records, before filtering (i.e. the total number of records in the database)
     * <p/>
     * (NB: I changed this to long)
     */
    private long recordsTotal;

    /**
     * Total records, after filtering (i.e. the total number of records after filtering has been applied - not just the number of records being returned for this
     * page of data).
     * <p/>
     * (NB: I changed this to long)
     */
    private long recordsFiltered;

    /**
     * Optional: If an error occurs during the running of the server-side processing script, you can inform the user of this error by passing back the error message
     * to be displayed using this parameter. Do not include if there is no error.
     */
    private String error;

    /**
     * The data to be displayed in the table. This is an array of data source objects, one for each row, which will be used by DataTables. Note that this parameter's
     * name can be changed using the ajax option's dataSrc property.
     */
    private List<T> data = Lists.newArrayList();
}
Euton answered 22/1, 2016 at 17:5 Comment(4)
Can this be done using GET or POST is easier to implement?Rickard
GET or POST it's your choice, no?Euton
Yes, but datatables when using GET are passing url which isnt't trivial to parse using @RequestParams, etc., this part: &order[0][column]=1&order[0][dir]=descRickard
This works, but in ideal scenario, should be a GET, not a POST. This is because this is just a "list" REST operation... That's why my question was posted in the first place. With a POST, the problem in the question doesn't exists...Septicemia
M
1

How about to implement HandlerMethodArgumentResolver, please refer to https://github.com/rakurakupg/springmvc-jquerydatatables-example

Melitta answered 16/1, 2016 at 12:35 Comment(0)
H
0

I had similar issue and below worked for me. Try adding @JsonProperty to each property in every parameter binding class e.g. @JsonProperty(value = "draw"). This way Jackson maps it fine with the JSON sent from DataTable. In my case, I was getting below JSON

{   
"draw":1,
"columns":[
    {"data":0,"name":"","searchable":true,"orderable":false,"search":{"value":"","regex":false}},
    {"data":1,"name":"","searchable":true,"orderable":false,"search":{"value":"","regex":false}}],
"order":[],
"start":0,
"length":-1,
"search":{"value":"","regex":false}

}

Also there is a need to have the empty constructor defined for DataTablesRequest, Column,Order and Search. If you do not want to have the default constructor, you can add it like below,

public Search(@JsonProperty("value") String value,@JsonProperty("regex") String regex) {
        // TODO Auto-generated constructor stub
    }

Hope this helps!

Hyo answered 28/1, 2015 at 21:50 Comment(2)
Forgot to mention, I am using Jackson 1.9.9 and DataTable 1.10.Hyo
That didn't worked out. I've tried to put the annotation on all properties but it didn't work. The problem is the Spring binding.Septicemia
P
0

The solution to this for me was to send the request as JSON by following the instructions here: ASP.net jQuery DataTables

Essentially just change the JS on your page to something like this:

'ajax': {
    'url': 'serverSideTableProviderPage',
    'type': 'POST',
    'data':function(data)
     {
       return data = JSON.stringify(data);
     }
},
Pourparler answered 27/5, 2015 at 12:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.