Предисловие
У меня есть приложение с пружинной загрузкой с сущностью User
с набором Role
.
В редактируемом шаблоне пользователя яотображение ролей пользователя с кратным числом <select>
.При повторном представлении существующего User
с его набором Role
я пытаюсь отметить только выбранные роли в наборе.
Thymeleaf предоставляет два инструмента для этого:
th:selected
: ожидание логического значения (при выборе true)
#sets
: предоставление нескольких полезных методов, подобных java.util.Set
, в данном случае используется contains()
.
Проблема
При добавлении к модели найденного User
и всех возможных Role
в виде HashSet, использование #sets.contains()
всегда возвращаетfalse при использовании ролей найденного пользователя и всех ролей в качестве параметров, поэтому не выбирайте роли пользователя при загрузке формы.
Если я использую нотацию th:selected="${{user.roles}}"
, все опции выбираются (даже те, которые пользовательне обладает).
Код
Пользователь
public class User
{
private Long id;
private String username;
private String password;
private String passwordConfirm;
private Set<Role> roles;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
@Transient
public String getPasswordConfirm()
{
return passwordConfirm;
}
public void setPasswordConfirm(String passwordConfirm)
{
this.passwordConfirm = passwordConfirm;
}
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "users_role", joinColumns = @JoinColumn(name = "users_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
public Set<Role> getRoles()
{
return roles;
}
public void setRoles(Set<Role> roles)
{
this.roles = roles;
}
}
Роль
public class Role
{
private Long id;
private String name;
private Set<User> users;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
@ManyToMany(mappedBy = "roles")
public Set<User> getUsers()
{
return users;
}
public void setUsers(Set<User> users)
{
this.users = users;
}
}
Контроллер
@Controller
@RequestMapping("/admin")
public class AdminController
{
@Autowired
UserService userService;
@Autowired
RoleService roleService;
@RequestMapping("/user/edit/{id}")
public String editUser(Model model, @PathVariable("id") long id)
{
User user = userService.findByUserId(id);
HashSet<Role> foundRoles = roleService.getAllRoles();
model.addAttribute("user", user);
model.addAttribute("userRoles", foundRoles);
return "admin/adminUserDetail";
}
}
Форма
<form role="form" th:action="@{/registration}" method="POST"
th:object="${user}">
<div th:if="${#fields.hasErrors('*')}">
<div class="alert alert-danger" role="alert">
<h3 class="alert-heading">It seems we have a couple problems with your input</h3>
<li th:each="err : ${#fields.errors('*')}" th:text="${err}"></li>
</div>
</div>
<div class="form-group">
<label>Username: </label> <input class="form-control" type="text" th:field="${user.username}"
placeholder="Username" name="username"/>
<label>Password: </label> <input class="form-control" type="password" th:field="${user.password}"
placeholder="Password" name="password"/>
<label>Password Confirm: </label> <input type="password"
th:field="${user.passwordConfirm}" class="form-control"
placeholder="Password Confirm"/>
<select class="form-control" multiple="multiple">
<option th:each="role : ${userRoles}"
th:value="${role.id}"
th:selected="${#sets.contains(user.roles, role)}"
th:text="${role.name}">Role name
</option>
</select>
<button type="submit" class="btn btn-success">Update</button>
</div>
</form>