0

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?

catch23
  • 17,519
  • 42
  • 144
  • 217
  • What do you mean by "it didn't work"? Also, I would just look for '?' instead of 'filter'. (But most probably, I would avoid putting together urls in a template altogether.) – ddekany Nov 29 '20 at 10:04
  • 1
    As a side note, `page.getNumber()`, and all these get stuff, can be written as `page.number`. – ddekany Nov 29 '20 at 10:06
  • @ddekany I didn't do what I want to achieve -> when set and filter has value I want to cocat pagination like `&....`. If there is no filter param concat it like `?....` – catch23 Nov 29 '20 at 10:38
  • Are you actually redirected, or simply that URL is what the #2 link points to? In which case, it seems that the `url` variable doesn't contain the `?filter=...` part. Not sure where the value of `url` is coming from. – ddekany Dec 01 '20 at 18:34

1 Answers1

1

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)

catch23
  • 17,519
  • 42
  • 144
  • 217
ddekany
  • 29,656
  • 4
  • 57
  • 64
  • Pagination is stopped to work after this update. Updated answer with code looking. – catch23 Nov 29 '20 at 11:37
  • 1
    @catch23 Remove the `<#if url?contains('filter')>` tag and the corresponding `#if>` tag. My point was that you don't need `#if`, instead you can use the other thing with the `?then`. Also, your chances of getting answers will be much higher if you tell what exactly happened, instead of "stopped working" and such. – ddekany Nov 29 '20 at 17:19