Таблицы данных JQuery с серверной обработкой весной (с Java и Thymeleaf) - PullRequest
0 голосов
/ 12 апреля 2019

У меня есть приложение Spring Boot, использующее Java и Thymeleaf. В нем у меня есть страница с JQuery DataTable. В этой таблице тысячи строк, и поэтому в настоящее время я просто помещаю все это на страницу и позволяю JQuery полностью ее обрабатывать, но я хочу переключиться на использование обработки ServerSide для разбивки на страницы, сортировки и т. Д.

Я настроен на выполнение этого на бэк-энде, где я могу передать начальный и конечный номер строки и динамически сортировать информацию в свой SQL-запрос. Однако моя проблема в том, что я просто не могу понять, как именно DataTables пытается уведомить меня через контроллер, когда пользователь нажимает кнопку «Следующая страница» или кнопку сортировки. Где / как мой контроллер может получить эту информацию, чтобы я мог вставить ее в свой SQL-запрос и вернуть то, что необходимо?

Итак, допустим, у меня есть пример объект , например, «Персона».

public class Person {

    private static String PERSON_IMAGE = "<img th:src=\"@{/images/personimage.png}\" alt=\"icon\"/>";
    private static String PERSON_IMAGE_2 = "<img th:src=\"@{/images/personimage2.png}\" alt=\"icon\"/>";

    private String name;
    private String socialSecurity;
    private String birthdate;
    private String gender;
    private String personImage;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSocialSecurity() {
        return socialSecurity;
    }

    public void setSocialSecurity(String socialSecurity) {
        this.socialSecurity = socialSecurity;
    }

    public String getBirthdate() {
        return birthdate;
    }

    public void setBirthdate(String birthdate) {
        this.birthdate = birthdate;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getPersonImage() {
        if(null != birthdate) {
            return PERSON_IMAGE;
        } else {
            return PERSON_IMAGE_2;
        }

    }
}

Итак, на моей странице Thymeleaf HTML у меня было следующее: (Версия 1)

<table id="myDataTable" class="dataTable display compact">
    <thead>
        <tr>
            <th>Name</th>
            <th>Social Security</th>                                        
            <th>Birthdate</th>
            <th>Gender</th>
        </tr>
    </thead>
    <tbody>
        <tr th:each="person : ${peopleList}">
            <td th:attr="data-order=${person.name}"><a th:href="|javascript:openPersonDetail('${person.socialSecurity}');|"><span th:text="${person.name}">name</span></a></td>
            <td th:text="${person.socialSecurity}">socialSecurity</td>                      
            <td class="dt-body-center" th:text="${person.birthdate}">birthdate</td>
            <td class="dt-body-center" th:text="${person.gender}">gender</td>
        </tr>
    </tbody>
</table>

Моя страница контроллер был очень прост и просто передан в полном списке людей следующим образом: (Версия 1)

@GetMapping("/ApplicationName/MyDataTablePage")
public String myDataTablePage(Model model) {

    SearchCriteria searchCriteria = new SearchCriteria();
    searchCriteria.setOrderByString("name");
    searchCriteria.setUsePaging(false);
    searchCriteria.setFIRST_NUMBER(null);
    searchCriteria.setLAST_NUMBER(null);

    model.addAttribute("peopleList", myMapperService.getPeople(searchCriteria)); //returns an ArrayList<Person>

    return "/pages/MyDataTablePage";
}

И, наконец, мой Javascript имел следующее: (Версия 1)

$(document).ready(function() {
    $('#myDataTable').DataTable({
        "destroy" : true,
        "scrollY" : 300,
        "scrollCollapse" : true,
        "paging" : true,
        "autoWidth" : true,
        "ordering" : true,
        "searching" : false,
        "order" : [ [ 0, 'asc' ] ],
        "pageLength" : 20,
        "lengthChange" : false,
        "pagingType" : "full_numbers",
        "dom" : '<"top"ip>rt<"bottom"fl><"clear">'
    });
});

Это сработало, но я хотел переключить DataTable для использования серверной обработки, и поэтому первым шагом было (1) изменить контроллер главной страницы, чтобы прекратить добавлять список людей в модель, (2) создать новый контроллер, который будет возвращать мой ArrayList в виде JSON, чтобы DataTable вызывал сам по себе, (3) изменить HTML-код главной страницы, чтобы больше не предоставлять тег tbody для DataTable, и (4) измените javascript, в котором создается таблица, для помещения данных в нее.

=============================================== ==============

Это привело меня к версии 2 из следующих элементов.

Мой измененный Thymeleaf HTML Страница: (Версия 2)

<table id="myDataTable" class="dataTable display compact">
    <thead>
        <tr>
            <th>Name</th>
            <th>Social Security</th>                                        
            <th>Birthdate</th>
            <th>Gender</th>
        </tr>
    </thead>
</table>

Моя измененная страница контроллер : (версия 2)

@GetMapping("/ApplicationName/MyDataTablePage")
public String myDataTablePage(Model model) {

    return "/pages/MyDataTablePage";
}

Мои новые DataTables контроллер :

@RequestMapping(path = "/ApplicationName/Data/Person", method = RequestMethod.GET, produces = "application/json")
@ResponseBody
public List<Person> getPersonData() {
    System.out.println("I was called!");

    SearchCriteria searchCriteria = new SearchCriteria();
    searchCriteria.setOrderByString("name");
    searchCriteria.setUsePaging(false);
    searchCriteria.setFIRST_NUMBER(null);
    searchCriteria.setLAST_NUMBER(null);

    return myMapperService.getPeople(searchCriteria); //returns an ArrayList<Person>
}

Мой измененный (гораздо более сложный) Javascript: (Версия 2)

$(document).ready(function () {
     $('#myDataTable').DataTable({ 
         'destroy' : true,
         'serverSide' : true,
         'sAjaxSource': '/ApplicationName/Data/Person',
         'sAjaxDataProp': '',
         'order': [ [ 0, 'asc' ] ],
         'columns': 
         [ 
            {  'data': 'name',
                'render': function(data, type, row, meta){ 
                    if(type === 'display'){ 
                        data = '<a href="javascript:openPersonDetail(&apos;'+ row.socialSecurity +'&apos;);">' + data + '</a>' 
                    }  
                    return data; 
                } 
            } ,
            { 'data': 'socialSecurity'} ,
            { 'data': 'birthdate'} ,
            { 'data': 'gender'} 
         ],
         'scrollY' : 300,
         'scrollCollapse' : true,
         'paging' : true,
         'autoWidth' : true,
         'ordering' : true,
         'searching' : false,
         'pageLength' : 20,
         'lengthChange' : false,
         'pagingType' : 'full_numbers',
         'dom' : '<"top"ip>rt<"bottom"fl><"clear">' 
     }); 
 });

=============================================== ==============

И это тоже работает. Особенно, если я удаляю конфигурацию «serverSide»: true, - DataTable работает точно так же, как раньше, до того как я сделал вышеупомянутые изменения, но теперь он вызывает мой новый контроллер Data JSON вместо использования атрибута модели.

Проблема:

Однако, с конфигурацией «serverSide»: true, добавленной в DataTable, каждый раз, когда я захожу на новую «страницу» таблицы, она просто вызывает мой контроллер «/ Clinic / Detail / Data / Patient» каждый раз.

Но мне нужны DataTables, чтобы передать моему контроллеру информацию о том, какую страницу выбрал пользователь или по какому столбцу пользователь пытался отсортировать. Мне также нужно передать в таблицу (динамически) общее количество строк (количество), чтобы DataTables могли правильно настроить номера страниц для пользователя.

Отправляет ли DataTables некоторые дополнительные параметры моему контроллеру, которые я могу подобрать? Что-то вроде @PathVariable («что угодно»)?

Я чувствую, что я так близок к тому, чтобы заставить это работать, но я застрял в этой точке.

Буду очень признателен за любую помощь!

1 Ответ

0 голосов
/ 15 апреля 2019

Мне удалось решить мою проблему.

Мой HTML для таблицы остался прежним, как показано ниже:

<table id="myDataTable" class="dataTable display compact">
    <thead>
        <tr>
            <th>Name</th>
            <th>Social Security</th>                                        
            <th>Birthdate</th>
            <th>Gender</th>
        </tr>
    </thead>
</table>

Мой Контроллер длястраница осталась прежней, как показано ниже:

@GetMapping("/ApplicationName/MyDataTablePage")
public String myDataTablePage(Model model) {

    return "/pages/MyDataTablePage";
}

My Контроллер для таблицы , которая извлекает данные, необходимые для изменения.Во-первых, нужно было получить @RequestParam, который отправляли таблицы данных, которые включали все параметры, которые мне были нужны, относительно того, сколько записей мне нужно показать, какую запись мне нужно отсортировать, и по какой ячейке сортируется.Во-вторых, вместо того, чтобы просто возвращать List<Person>, нужно было вернуть правильно сформированный объект JSON с определенными параметрами, которые Datatable использует для определения количества страниц, отображаемых на экране, а также сами данные List<Person>.

@RequestMapping(path = "/ApplicationName/Data/Person", method = RequestMethod.GET, produces = "application/json")
@ResponseBody
public TableBean getPersonData(@RequestParam Map<String, String> allRequestParams) {

    TableParameters tableParameters = myMapperService
            .getTableParameters(allRequestParams);

    return myMapperService.mapTableBean(tableParameters); //returns an TableBean which contains the ArrayList<Person>
}

Чтобы это работало, мне нужно было создать несколько новых классов и методов.Сначала я создал бин TableParameters , который содержит все параметры для моей таблицы данных:

public class TableParameters implements Serializable {
    private static final long serialVersionUID = 1L;

    private Integer iDisplayStart;

    private Integer iDisplayLength;

    private Integer iColumns;

    private String sSearch;

    private Boolean bRegex;

    private Integer iSortingCols;

    private Integer sEcho;

    private Integer sortedColumnNumber;

    private String sortedColumnName;

    private String sortedColumnDirection;


    // ... getters and setters

}

И затем мне нужно было создать метод , который бы вытягивалэти переменные вне запроса Parms и сопоставить их с новым компонентом.Это говорит мне, что использовать в моем SQL, как сортировать и какие числа начинать и возвращать.

private static String iDisplayLength = "iDisplayLength";
private static String iDisplayStart = "iDisplayStart";
private static String iColumns = "iColumns";
private static String sSearch = "sSearch";
private static String bRegex = "bRegex";
private static String iSortingCols = "iSortingCols";
private static String sEcho = "sEcho";
private static String iSortCol_0 = "iSortCol_0";
private static String sSortDir_0 = "sSortDir_0";

public TableParameters getTableParameters(Map<String, String> allRequestParams) {

    TableParameters finalBean = new TableParameters();

    if (null != allRequestParams.get(bRegex)) {
        finalBean.setbRegex(new Boolean(allRequestParams.get(bRegex)));
    }
    if (null != allRequestParams.get(iColumns)) {
        finalBean.setiColumns(new Integer(allRequestParams.get(iColumns)));
    }
    if (null != allRequestParams.get(iDisplayLength)) {
        finalBean.setiDisplayLength(new Integer(allRequestParams.get(iDisplayLength)));
    }
    if (null != allRequestParams.get(iDisplayStart)) {
        finalBean.setiDisplayStart(new Integer(allRequestParams.get(iDisplayStart)));
    }
    if (null != allRequestParams.get(iSortingCols)) {
        finalBean.setiSortingCols(new Integer(allRequestParams.get(iSortingCols)));
    }
    if (null != allRequestParams.get(sEcho)) {
        try {
            finalBean.setsEcho(new Integer(allRequestParams.get(sEcho)));
        } catch (Exception e) {
            // ignore
        }
    }
    if (null != allRequestParams.get(sSearch)) {
        finalBean.setsSearch(allRequestParams.get(sSearch));
    }

    int numberOfColumnsSortedOn = 0;
    if (allRequestParams.containsKey(iSortingCols) && null != allRequestParams.get(iSortingCols)) {
        numberOfColumnsSortedOn = new Integer(allRequestParams.get("iSortingCols")).intValue();
    }

    if (numberOfColumnsSortedOn > 0) {
        if (null != allRequestParams.get(iSortCol_0)) {
            finalBean.setSortedColumnNumber(new Integer(allRequestParams.get(iSortCol_0)));
        }
        if (null != allRequestParams.get(sSortDir_0)) {
            finalBean.setSortedColumnDirection(allRequestParams.get(sSortDir_0));
        }
        String keyForName = "mDataProp_" + finalBean.getSortedColumnNumber();
        if (null != allRequestParams.get(keyForName)) {
            finalBean.setSortedColumnName(allRequestParams.get(keyForName).toUpperCase());
        }
    }
    return finalBean;
}

Аналогично, Return json объект TableBean должен бытьсозданный, который имеет не только List<Person> на нем, но также и необходимую информацию о количестве, используемую датированными для отображения номеров страниц пользователю.Это то, что теперь возвращается в методе контроллера таблицы, а не просто в базовом списке.

public class TableBean implements Serializable {
    private static final long serialVersionUID = 1L;

    private int iTotalRecords;

    private int iTotalDisplayRecords;

    private String sEcho;

    private List data;

    public TableBean(int iTotalRecords, int iTotalDisplayRecords, String sEcho, List data) {
        super();
        this.iTotalRecords = iTotalRecords;
        this.iTotalDisplayRecords = iTotalDisplayRecords;
        this.sEcho = sEcho;
        this.data = data;
    }

    // ... getters and setters
}

И метод mapTableBean , который объединяет все это, фактически вызывая базу данных, чтобы получитьзаписи.

public TableBean mapTableBean(TableParameters tableParameters) {
    int iTotalRecords = database.getCount();
    int iTotalDisplayRecords = iTotalRecords;
    if (null != tableParameters.getsEcho()){
        String sEcho = tableParameters.getsEcho().toString(); 
    }
    List data = database.search(tableParameters.getiDisplayStart(), tableParameters.getiDisplayLength(),
            tableParameters.getSortedColumnName(),
            tableParameters.getSortedColumnDirection().equalsIgnoreCase("asc"));

    return new TableBean(iTotalRecords, iTotalDisplayRecords, sEcho, data);
}

Наконец, Javascript нужно было изменить, чтобы DataTables знал, что нужно взглянуть на переменную с именем 'data' для ее списка Beans, показанного в самой таблице,добавив конфигурацию 'sAjaxDataProp': 'data',.

$(document).ready(function () {
     $('#myDataTable').DataTable({ 
         'destroy' : true,
         'serverSide' : true,
         'sAjaxSource': '/ApplicationName/Data/Person',
         'sAjaxDataProp': 'data',
         'order': [ [ 0, 'asc' ] ],
         'columns': 
         [ 
            {  'data': 'name',
                'render': function(data, type, row, meta){ 
                    if(type === 'display'){ 
                        data = '<a href="javascript:openPersonDetail(&apos;'+ row.socialSecurity +'&apos;);">' + data + '</a>' 
                    }  
                    return data; 
                } 
            } ,
            { 'data': 'socialSecurity'} ,
            { 'data': 'birthdate'} ,
            { 'data': 'gender'} 
         ],
         'scrollY' : 300,
         'scrollCollapse' : true,
         'paging' : true,
         'autoWidth' : true,
         'ordering' : true,
         'searching' : false,
         'pageLength' : 20,
         'lengthChange' : false,
         'pagingType' : 'full_numbers',
         'dom' : '<"top"ip>rt<"bottom"fl><"clear">' 
     }); 
 });

И вуаля, теперь обработка на стороне сервера работает.

...