Warm tip: This article is reproduced from serverfault.com, please click

Freemarker pagination with Search parameter

发布于 2020-11-28 20:57:17

I have working pagination with Fremarker. Here is pager makros:

<#macro pager url page>
    <#if page.getTotalPages() gt 7>
        <#assign
        totalPages = page.getTotalPages()
        pageNumber = page.getNumber() + 1

        head = (pageNumber > 4)?then([1, -1], [1, 2, 3])
        tail = (pageNumber < totalPages - 3)?then([-1, totalPages], [totalPages - 2, totalPages - 1, totalPages])
        bodyBefore = (pageNumber > 4 && pageNumber < totalPages - 1)?then([pageNumber - 2, pageNumber - 1], [])
        bodyAfter = (pageNumber > 2 && pageNumber < totalPages - 3)?then([pageNumber + 1, pageNumber + 2], [])

        body = head + bodyBefore + (pageNumber > 3 && pageNumber < totalPages - 2)?then([pageNumber], []) + bodyAfter + tail
        >
    <#else>
        <#assign body = 1..page.getTotalPages()>
    </#if>
    <div class="container-fluid mt-3">
        <div class="row">
            <ul class="pagination col">
                <li class="page-item disabled">
                    <a class="page-link" href="#" tabindex="-1">Pages</a>
                </li>
                <#list body as p>
                    <#if (p - 1) == page.getNumber()>
                        <li class="page-item active">
                            <a class="page-link" href="#" tabindex="-1">${p}</a>
                        </li>
                    <#elseif p == -1>
                        <li class="page-item disabled">
                            <a class="page-link" href="#" tabindex="-1">...</a>
                        </li>
                    <#else>
                        <li class="page-item">
                            <a class="page-link" href="${url}?page=${p - 1}&size=${page.getSize()}" tabindex="-1">${p}</a>
                        </li>
                    </#if>
                </#list>
            </ul>

            <ul class="pagination col justify-content-end">
                <li class="page-item disabled">
                    <a class="page-link" href="#" tabindex="-1">Elements per page</a>
                </li>
                <#list [20, 40, 80, 120] as c>
                    <#if c == page.getSize()>
                        <li class="page-item active">
                            <a class="page-link" href="#" tabindex="-1">${c}</a>
                        </li>
                    <#else>
                        <li class="page-item">
                            <a class="page-link" href="${url}?page=${page.getNumber()}&size=${c}" tabindex="-1">${c}</a>
                        </li>
                    </#if>
                </#list>
            </ul>
        </div>
    </div>
</#macro>

And pagination URL looks like:

http://localhost:8080/main?page=1&size=10

There is search functionality for the main page:

<@c.page>
    <div class="form-row">
        <div class="form-group col-md-6">
            <form method="get" action="/main" class="form-inline">
                <input type="text" name="filter" class="form-control" value="${filter?ifExists}"
                       placeholder="Search by tag"/>
                <button type="submit" class="btn btn-primary ml-2">Search</button>
            </form>
        </div>
    </div>
</@c.page>

if the search is entered, URL will look like:

http://localhost:8080/main?filter=search-param

However, pagination doesn't work with URL which has entered search param (it is called filter actually).

Tried to add something like following to pager.ftl:

<#if '${url}'?contains("filter")>
    <a class="page-link" href="${url}&page=${p - 1}&size=${page.getSize()}" tabindex="-1">${p}</a>
<#else>
    <a class="page-link" href="${url}?page=${p - 1}&size=${page.getSize()}" tabindex="-1">${p}</a>
</#if>

The idea was check URL if filter param is set -> add pagination to it:

http://localhost:8080/main?filter=search-param&page=1&size=10

If there is no search param, use the default one.

And it didn't work.

Here are full Project Sources at GitHub.

Backend logic with Spring Boot and Spring Data work pretty well.


Tried solution by @ddekany. Here is the result:

<#else>
    <li class="page-item">    
        <a class="page-link"
           href="${url}${url?contains('?')?then('&', '?')}page=${p - 1}&size=${page.getSize()}"
           tabindex="-1">
            ${p}
        </a>
    </li>
</#if>

and the second part:

<#else>
    <li class="page-item">
        <a class="page-link"
           href="${url}${url?contains('?')?then('&', '?')}page=${page.getNumber()}&size=${c}"
           tabindex="-1">
            ${c}
        </a>
    </li>
</#if>

Here is when you enter the search:

enter image description here

However, when you press 2 page of a search you will be redirected to the main page (search is empty instead of going to #2 page of search results):

enter image description here


SOLUTION:

Correct setting the URL at controller:

Page<MessageDto> page = messageService.messageList(pageable, filter, user);

if (StringUtils.isNotBlank(filter)) {
    model.addAttribute("url", String.format("/main?filter=%s", filter));
} else {
    model.addAttribute("url", "/main");
}

model.addAttribute("page", page);
model.addAttribute("filter", filter);

return "main";

and change set URL at pager:

<li class="page-item">
    <#--<a class="page-link" href="${url}?page=${p - 1}&size=${page.getSize()}" tabindex="-1">${p}</a>-->
    <a class="page-link" href="${url}${url?contains('?')?then('&', '?')}page=${p - 1}&size=${page.getSize()}" tabindex="-1">
        ${p}
    </a>
</li>

and

<li class="page-item">
    <#--<a class="page-link" href="${url}?page=${page.getNumber()}&size=${c}" tabindex="-1">${c}</a>-->
    <a class="page-link" href="${url}${url?contains('?')?then('&', '?')}page=${page.getNumber()}&size=${c}" tabindex="-1">
        ${c}
    </a>
</li>

Commented lines are old code. Uncommented is exactly working.

Question is how to configure Fremarker for such functionality?

Questioner
catch23
Viewed
0
13.5k 2020-11-29 19:13:22

Check again, because that #if should work.

However, I would look for ?, as it's more robust than looking for filter, as it works even if the parameter is later renamed.

Also, <#if '${url}'?contains('...')> can be just written as <#if url?contains('...')>.

Actually, you probably don't want to repeat that whole line, only to replace a single character, so just write

href="${url}${url?contains('?')?then('&', '?')}page=....

(You can test out such things quickly on Freemarker demo)