Validation with SpringBoot, Thymeleaf and Bootstrap

Viewed 305

I'm just start learning validation of users input value via modal window(bootstrap). And now I have a problem with controller/template, it doesn't work- even if I open /groups/getAll page, server drops with error:

 Failed to complete request: org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "#fields.hasErrors('name')" (template: "groups/getAll" - line 61, col 59)
20:32:50.056 [http-nio-8080-exec-1] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] -
                Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "#fields.hasErrors('name')" (template: "groups/getAll" - line 61, col 59)] with root cause
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'group' available as request attribute

In console I see that constraint works well - @UniqueGroupName validator done as I want. How to fix controller and template to correct work?

Thanks in advance.

GroupDto

@UniqueGroupName
public class GroupDto {

    private int id;
    @NotBlank(message = "Name is mandatory")
    @Pattern(regexp = "[A-Z]{2}-[0-9]{3}", message = "Invalid Group name format, should be \"AA-201\"")
    private String name;
    @NotNull(message = "Select faculty")
    private int facultyId;
    private String facultyName;

GroupController

@Controller
@RequestMapping("/groups")
public class GroupController {

private static final String REDIRECT_PAGE = "redirect:/groups/getAll";

private final GroupService groupService;
private final ModelMapper modelMapper;
private final Logger logger = LoggerFactory.getLogger(GroupController.class);

@Autowired
public GroupController(GroupService groupService, ModelMapper modelMapper) {
    this.groupService = groupService;
    this.modelMapper = modelMapper;
}

@GetMapping("/getAll")
public String findAllGroups(Model model) {
    logger.debug("findAll() groupDtos");
    List<GroupDto> groups = groupService.findAll().stream().map(g -> convertToDto(g)).collect(Collectors.toList());
    logger.trace("found {} groups.", groups.size());
    model.addAttribute("groups", groups);
    List<Faculty> faculties = groupService.getFaculties();
    model.addAttribute("faculties", faculties);
    return "groups/getAll";
}

@PostMapping("/add")
public String add(@Valid @ModelAttribute(value= "group") GroupDto groupDto, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        //what I need here to return modal window with errors information?
    } else {
        groupService.add(convertToEntity(groupDto));
        return REDIRECT_PAGE;
    }
}
 @GetMapping("/find")
    @ResponseBody
    public GroupDto find(int id) {
        return convertToDto(groupService.find(id));
    }

@PostMapping(value = "/update")
public String update(@Valid GroupDto groupDto) {
    groupService.update(convertToEntity(groupDto));
    return REDIRECT_PAGE;
}

@GetMapping("/delete")
public String deleteUser(int id) {
    groupService.delete(id);
    return REDIRECT_PAGE;
}

private GroupDto convertToDto(Group group) {
    return modelMapper.map(group, GroupDto.class);
}

private Group convertToEntity(GroupDto groupDto) {
    return modelMapper.map(groupDto, Group.class);
}
}

And finally template:

<!DOCTYPE html>
<html>
<head>
<div th:insert="/fragments/common :: common_head"></div>
<script type="text/javascript" src="../../resources/js/groups.js" th:src="@{/resources/js/groups.js}"></script>
<script type="text/javascript" src="../../resources/js/common.js" th:src="@{/resources/js/common.js}"></script>
<title>Groups</title>
</head>
<body class="bg-secondary">
    <div class="container">
        <H2 class="text-body">List of groups</H2>
    </div>

<div>
    <nav class="navbar navbar-expand-sm bg-success navbar-light">
        <ul class="navbar-nav  text-white">
            <li class="nav-item"><a class="nav-link" th:href="@{/main_page}">Home</a></li>
            <li class="nav-item"><a class="nav-link" th:href="@{/buildings/getAll}">Buildings</a></li>
            <li class="nav-item"><a class="nav-link" th:href="@{/auditoriums/getAll}">Auditoriums</a></li>
            <li class="nav-item"><a class="nav-link active" href="#">Groups</a></li>
            <li class="nav-item"><a class="nav-link" th:href="@{/faculties/getAll}">Faculties</a></li>
        </ul>
    </nav>
</div>

<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addModal">Add group</button>

<div>
    <table class="table table-bordered">
        <thead class="bg-primary text-white">
            <tr>
                <td><strong>Group name</strong></td>
                <td><strong>Faculty Name</strong></td>
                <td><strong>Edit</strong></td>
                <td><strong>Delete</strong></td>
            </tr>
        </thead>
        <tr th:each="group:${groups}" class="table-success">
            <td th:text="${group.name}"></td>
            <td th:text="${group.facultyName}"></td>
            <td><a th:href="@{/groups/find/(id=${group.id})}" class="btn btn-warning">Edit</a></td>
            <td><a th:href="@{/groups/delete/(id=${group.id})}" class="btn btn-danger">Delete</a></td>
        </tr>
    </table>
</div>

<div class="modal fade" id="addModal" tabindex="-1" role="dialog" aria-labelledby="addModal" aria-hidden="true">
    <form action="#" th:action="@{/groups/add}" method="post" th:object="${group}">
        <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title" id="addModal">Add group</h5>
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                        <span aria-hidden="true">&times;</span>
                    </button>
                </div>
                <div class="modal-body">
                    <div class="form-group">
                        <label for="name" class="col-form-label">Group Name</label> <input type="text"
                            class="form-control" id="addName" name="name" placeholder="Group Name">
                        <div class="text text-danger" th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></div>
                    </div>
                    <div class="form-group">
                        <label for="faculyId" class="col-form-label">Faculty</label> <select class="form-control"
                            id="addFaculty" name="facultyId">
                            <option th:value=0>Faculty not assign</option>
                            <option th:each="faculty : ${faculties}" th:value="${faculty.id}"
                                th:utext="${faculty.name}">Faculty</option>
                        </select>
                        <div class="text text-danger" th:if="${#fields.hasErrors('facultyId')}"
                            th:errors="*{facultyId}"></div>
                    </div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
                    <button type="submit" class="btn btn-primary">Save</button>
                </div>
            </div>
        </div>
    </form>
</div>

<div class="modal fade" id="editModal" tabindex="-1" role="dialog" aria-labelledby="editModal" aria-hidden="true">
    <form th:action="@{/groups/update}" method="post">
        <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title" id="editModalLabel">Edit building</h5>
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                        <span aria-hidden="true">&times;</span>
                    </button>
                </div>
                <div class="modal-body">
                    <div class="form-group">
                        <label for="recipient-name" class="col-form-label">Group Id</label> <input type="text"
                            class="form-control" id="idEdit" name="id" readonly>
                    </div>
                    <div class="form-group">
                        <label for="recipient-name" class="col-form-label">Group Name</label> <input type="text"
                            class="form-control" id="nameEdit" name="name">
                    </div>
                    <div class="form-group">
                        <label for="facultyId" class="col-form-label">Faculty</label> <select class="form-control"
                            id="facultyEdit" name="facultyId">
                            <option th:each="faculty : ${faculties}" th:value="${faculty.id}"
                                th:utext="${faculty.name}" th:selected="${faculty.id}==facultyEdit">Faculty</option>
                        </select>
                    </div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
                    <button type="submit" class="btn btn-primary">Save</button>
                </div>
            </div>
        </div>
    </form>
</div>

<div th:insert="fragments/common :: deleteEntry"></div>

0 Answers
Related