В приложении есть общая страница с сообщениями, где отображаются все сообщения всех пользователей, и отдельная страница (профиль пользователя), где видны только сообщения зарегистрированного пользователя.До того, как я добавил разбиение на страницы, все работало, но теперь все хорошо только на главной странице, сообщения и номера страниц видны, но если вы откроете страницу (профиль пользователя) с зарегистрированным пользователем, номера страниц и сообщенияне отображается, вместо этого страницы выдают ошибку Ошибка шаблона FreeMarker.
main.ftl - это главная страница со всеми сообщениями, на которой все работает, отображение всех сообщений описано в messageList.ftl.
Ошибка
ERROR 6588 --- [nio-8080-exec-5] freemarker.runtime : Error executing FreeMarker template
freemarker.core._MiscTemplateException: When calling macro "pager", required parameter "url" (parameter #1) was specified, but had null/missing value.
----
Tip: If the parameter value expression on the caller side is known to be legally null/missing, you may want to specify a default value for it with the "!" operator, like paramValue!defaultValue.
----
----
FTL stack trace ("~" means nesting-related):
- Failed at: #macro pager url page [in template "parts/pager.ftl" in macro "pager" at line 1, column 1]
- Reached through: @p.pager url, page [in template "parts/messageList.ftl" at line 4, column 1]
- Reached through: #include "parts/messageList.ftl" [in template "userMessages.ftl" at line 40, column 5]
~ Reached through: #nested [in template "parts/common.ftl" in macro "page" at line 18, column 1]
~ Reached through: @c.page [in template "userMessages.ftl" at line 3, column 1]
userMessages.ftl
<#import "parts/common.ftl" as c>
<@c.page>
<h3>${userChannel.username}</h3>
<#if !isCurrentUser>
<#if isSubscriber>
<a class="btn btn-info" href="/user/unsubscribe/${userChannel.id}">Unsubscribe</a>
<#else>
<a class="btn btn-info" href="/user/subscribe/${userChannel.id}">Subscribe</a>
</#if>
</#if>
<div class="container my-3">
<div class="row">
<div class="col">
<div class="card">
<div class="card-body">
<div class="card-title">Subscriptions</div>
<h3 class="card-text">
<a href="/user/subscriptions/${userChannel.id}/list">${subscriptionsCount}</a>
</h3>
</div>
</div>
</div>
<div class="col">
<div class="card">
<div class="card-body">
<div class="card-title">Subscribers</div>
<h3 class="card-text">
<a href="/user/subscribers/${userChannel.id}/list">${subscribersCount}</a>
</h3>
</div>
</div>
</div>
</div>
</div>
<#if isCurrentUser>
<#include "parts/messageEdit.ftl" />
</#if>
<#include "parts/messageList.ftl" />
</@c.page>
pager.ftl
<#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="mt-3">
<ul class="pagination">
<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">
<li class="page-item disabled">
<a class="page-link" href="#" tabindex="-1">Quantity</a>
</li>
<#list [5, 10, 25, 50] 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>
messageList.ftl
<#include "security.ftl">
<#import "pager.ftl" as p>
<@p.pager url page />
<div class="card-columns" id="message-list">
<#list page.content as message>
<div class="card my-3" data-id="${message.id}"/>
<#if message.filename??>
<img src="/img/${message.filename}" class="card-img-top" />
</#if>
<div class="m-2">
<span>${message.text}</span><br/>
<i>#${message.tag}</i>
</div>
<div class="card-footer text-muted">
<a href="/user-messages/${message.author.id}">${message.authorName}</a>
<#if message.author.id == currentUserId>
<a class="btn btn-primary" href="/user-messages/${message.author.id}?message=${message.id}">
Edit
</a>
</#if>
</div>
</div>
<#else>
No message
</#list>
</div>
<@p.pager url page />
common.ftl
<#macro page>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="stylesheet" href="/static/style.css" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous" />
<script src='https://www.google.com/recaptcha/api.js'></script>
</head>
<body>
<#include "navbar.ftl">
<div class="container mt-5">
<#nested>
</div>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js" integrity="sha384-smHYKdLADwkXOn1EmN1qk/HfnUcbVRZyYmZ4qpPea6sjB/pTJ0euyQp0Mk8ck+5T" crossorigin="anonymous"></script>
</body>
</html>
</#macro>
main.ftl
<#import "parts/common.ftl" as c>
<@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>
<#include "parts/messageEdit.ftl" />
<#include "parts/messageList.ftl" />
</@c.page>
MainController.java
package coding.domain.controller;
import coding.domain.domain.Message;
import coding.domain.domain.User;
import coding.domain.repos.MessageRepo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.Valid;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@Controller
public class MainController {
@Autowired
private MessageRepo messageRepo;
@Value("${upload.path}")
private String uploadPath;
@GetMapping("/")
public String greeting(Map<String, Object> model) {
return "greeting";
}
@GetMapping("/main")
public String main(
@RequestParam(required = false, defaultValue = "") String filter,
Model model,
@PageableDefault(sort = { "id" }, direction = Sort.Direction.DESC) Pageable pageable
) {
Page<Message> page;
if (filter != null && !filter.isEmpty()) {
page = messageRepo.findByTag(filter, pageable);
} else {
page = messageRepo.findAll(pageable);
}
model.addAttribute("page", page);
model.addAttribute("url", "/main");
model.addAttribute("filter", filter);
return "main";
}
@PostMapping("/main")
public String add(
@AuthenticationPrincipal User user,
@Valid Message message,
BindingResult bindingResult,
Model model,
@RequestParam("file") MultipartFile file
) throws IOException {
message.setAuthor(user);
if (bindingResult.hasErrors()) {
Map<String, String> errorsMap = ControllerUtils.getErrors(bindingResult);
model.mergeAttributes(errorsMap);
model.addAttribute("message", message);
} else {
saveFile(message, file);
model.addAttribute("message", null);
messageRepo.save(message);
}
Iterable<Message> messages = messageRepo.findAll();
model.addAttribute("messages", messages);
return "main";
}
private void saveFile(@Valid Message message, @RequestParam("file") MultipartFile file) throws IOException {
if (file != null && !file.getOriginalFilename().isEmpty()) {
File uploadDir = new File(uploadPath);
if (!uploadDir.exists()) {
uploadDir.mkdir();
}
String uuidFile = UUID.randomUUID().toString();
String resultFilename = uuidFile + "." + file.getOriginalFilename();
file.transferTo(new File(uploadPath + "/" + resultFilename));
message.setFilename(resultFilename);
}
}
@GetMapping("/user-messages/{user}")
public String userMessges(
@AuthenticationPrincipal User currentUser,
@PathVariable User user,
Model model,
@RequestParam(required = false) Message message
) {
Set<Message> messages = user.getMessages();
model.addAttribute("userChannel", user);
model.addAttribute("subscriptionsCount", user.getSubscriptions().size());
model.addAttribute("subscribersCount", user.getSubscribers().size());
model.addAttribute("isSubscriber", user.getSubscribers().contains(currentUser));
model.addAttribute("messages", messages);
model.addAttribute("message", message);
model.addAttribute("isCurrentUser", currentUser.equals(user));
return "userMessages";
}
@PostMapping("/user-messages/{user}")
public String updateMessage(
@AuthenticationPrincipal User currentUser,
@PathVariable Long user,
@RequestParam("id") Message message,
@RequestParam("text") String text,
@RequestParam("tag") String tag,
@RequestParam("file") MultipartFile file
) throws IOException {
if (message.getAuthor().equals(currentUser)) {
if (!StringUtils.isEmpty(text)) {
message.setText(text);
}
if (!StringUtils.isEmpty(tag)) {
message.setTag(tag);
}
saveFile(message, file);
messageRepo.save(message);
}
return "redirect:/user-messages/" + user;
}
}