0

I made a modal in my Bootstrap 4 view. But I get an error

ERROR 328 --- [nio-8080-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.thymeleaf.exceptions.TemplateProcessingException: Error during execution of processor 'org.thymeleaf.spring6.processor.SpringInputGeneralFieldTagProcessor' (template: "admin" - line 87, col 78)] with root cause

java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'user' available as request attribute
    at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:153) ~[spring-webmvc-6.0.7.jar:6.0.7]
    at org.springframework.web.servlet.support.RequestContext.getBindStatus(RequestContext.java:926) ~[spring-webmvc-6.0.7.jar:6.0.7]

Here's a code snippet from my HTML view. I left a comment to show you which line causes an error

    <tbody>
    <!-- remember this user object below, I'll be referencing it later -->
                            <tr th:each="user: ${users}">
                                <td th:text="${user.username}"></td>
                                <td th:text="${user.name}"></td>
                                <td th:text="${user.lastName}"></td>
                                <td th:text="${user.department}"></td>
                                <td th:text="${user.salary}"></td>
                                <td th:text="${user.age}"></td>
                                <td th:text="${user.email}"></td>
                                <td>
                                    <span th:each="role, iter : ${user.authorities}"
                                          th:text="${role.authority == 'USER' ? 'U' : (role.authority == 'ADMIN' ? 'A' : '')} + ${iter.last ? '' : ' '}"></span>
                                </td>
                                <td th:text="${user.isEnabled() ? '+' : '-'}"></td>
    <td class="btn-group btn-group-sm">
                                    <a class="btn btn-outline-primary" data-toggle="modal" th:data-target="${'#updateModal' + user.username}">Update</a>
                                    <div class="modal" th:id="${'updateModal' + user.username}">
                                        <div class="modal-dialog">
                                            <div class="modal-content">

                                                <div class="modal-header">
                                                    <h4 class="modal-title" th:text="${'Edit ' + user.name + ' ' + user.lastName + ' (' + user.username + ')'}"></h4>
                                                    <button type="button" class="close" data-dismiss="modal">&times;</button>
                                                </div>

                                                <div class="modal-body">
                                                    <p th:text="'For the sake of privacy, you are only allowed to change the user\'s department ' +
                                                        'and salary. If you think ' + ${user.name} + '\'s personal info needs to be altered, please request them to do so'"></p>

<!-- I also tried removing this th:object altogether ↓-->

                                                    <form th:action="@{/save-user}" th:object="${user}" method="post">
    
    <!-- below is line 87 that causes an error. the user I try to reference here is the same user from th:each="user: ${users} in the code above -->
    
                                                        <input type="hidden" th:field="${user.id}">

                                                        <input type="hidden" th:field="${user.username}">

                                                        <input type="hidden" th:field="${user.password}">

                                                        <input type="hidden" th:field="${user.name}">

                                                        <input type="hidden" th:field="${user.lastName}">

                                                        <fieldset>
                                                            <label for="departments">Department: </label>
                                                            <select id="departments" th:field="${user.department}">
                                                                <option th:value="accounting">Accounting</option>
                                                                <option th:value="sales">Sales</option>
                                                                <option th:value="'information technology'">IT</option>
                                                                <option th:value="'human resources'">HR</option>
                                                            </select>
                                                        </fieldset>

                                                        <fieldset>
                                                            <label for="salary">Salary: </label>
                                                            <input id="salary" th:field="${user.salary}"/>
                                                        </fieldset>

                                                        <input type="hidden" th:field="${user.age}">

                                                        <input type="hidden" th:field="${user.email}">

                                                        <input type="hidden" th:field="${user.enabledByte}">

                                                        <input type="hidden" th:name="authorities"
                                                               th:value="${#strings.toString(user.authorities)}"/>

                                                        <input class="main-button" type="submit" value="Submit">
                                                    </form>
                                                </div>
                                            </div>
                                        </div>
                                    </div>

For some reason, at line 87, Thymeleaf seemingly can't access that forEach user's fields anymore even though it's within the scope – the <tr> element that has the th:each attribute. Could you please tell me why that's so and how I can fix it?

The controller looks like this. As you see, there are no other attributes there that have the same name ("user")

    @GetMapping("/admin")
    public String admin(Model model, Authentication authentication) {
        model.addAttribute("users", service.getAllExceptLoggedUser(authentication))
                .addAttribute("loggedUser", service.getLoggedUser(authentication))
                .addAttribute("newUser", new User())
                .addAttribute("adminRoleSet", service.getAdminRoleSet());
        return "admin";
    }

Also, the IDE doesn't underline "user" anywhere in my view code so it apparently knows what user I'm talking about

I read these StackOverflow questions: 1, 2, 3, 4. They don't seem to have much to do with my question although they refer to the same error message

UPD: For some reason, changing, for example, this

<input type="hidden" th:field="${user.id}">

to this

<input type="hidden" name="id" th:value="${user.id}">

helped. It seems so anyway, I haven't properly tested it yet. At least, I am now able to see the view, the modals pop up. Feel free to share your ideas on what that "some" reason might be in the comments (or even write a full-fledged answer)

Though there's one but. I hoped a user's current department will be selected in the drop-down list. Instead, it's simply the first one

<fieldset>
     <label for="departments">Department: </label>
     <select id="departments" name="department" th:value="${user.department}">
                  <option th:value="accounting">Accounting</option>
                  <option th:value="sales">Sales</option>
                  <option th:value="'information technology'">IT</option>
                  <option th:value="'human resources'">HR</option>
     </select>
</fieldset>

The salary is displayed fine, on the other hand

<fieldset>
      <label for="salary">Salary: </label>
      <input id="salary" name="salary" th:value="${user.salary}"/>
</fieldset>

1 Answers1

0

You cannot use generated variables like ${user} from user: ${users} as a th:object for a form. From the docs:

Values for th:object attributes in form tags must be variable expressions (${...}) specifying only the name of a model attribute, without property navigation.

(Since ${user} is not the name of a model attribute, it fails this check.)

If you want to use multiple forms like you are doing, you can't use the built in th: attributes like object and field. You can to manually create them using th:name and th:value.


<select /> example:

<fieldset>
  <label for="departments">Department: </label>
  <select id="departments" name="department">
    <option th:selected="${user.department == 'accounting'}" value="accounting">Accounting</option>
    <option th:selected="${user.department == 'sales'}" value="sales">Sales</option>
    <option th:selected="${user.department == 'information technology'}" value="information technology">IT</option>
    <option th:selected="${user.department == 'human resources'}" value="human resources">HR</option>
  </select>
</fieldset>
Metroids
  • 18,999
  • 4
  • 41
  • 52