Reputation: 1436
In the following piece of code I just want to create a new user and link it to the selected groups.
Everything works fine when the user and group are valid. The problem comes when the bindingresult has errors. The controller detects such error (all fine so far) and returns the same view (I want to keep the data entered by the user) but the list of groups is empty (I have discovered that, after showing again the view, userform.groups is null).
Has anyone a clue about what the problem could be?
UserForm
@Component
public class UserForm {
@Valid
private User user;
@Valid
private Collection<Group> allGroups;
// Setters and getters
}
UserController
@Controller
public class UserController {
@Autowired
UserGroupService userGroupService;
@Autowired
BCryptPasswordEncoder passwordEncoder;
@InitBinder
public void initBinder (WebDataBinder binder) {
binder.registerCustomEditor(Set.class, "userform.user.groups", new GroupListEditor(userGroupService));
}
@RequestMapping(value = "/admin/users/CreateUser", method = RequestMethod.GET)
public ModelAndView createUsetGet () {
ModelAndView mav = new ModelAndView("/admin/users/CreateUser");
UserForm userForm = new UserForm();
userForm.setUser(new User());
userForm.setGroups(userGroupService.getAllEnabledGroups());
mav.addObject("userform", userForm);
return mav;
}
@RequestMapping(value = "/admin/users/CreateUser", method = RequestMethod.POST)
public String createUserPost (@Valid @ModelAttribute("userform") UserForm userForm, BindingResult result) {
if (result.hasErrors() == true) {
return "/admin/users/CreateUser";
}
userForm.getUser().setPassword(passwordEncoder.encode(userForm.getUser().getPassword()));
userGroupService.saveUser(userForm.getUser());
return "redirect:/admin/users/ViewUsers";
}
}
CreateUser.jsp (Only piece regarding the groups)
<form:form modelAttribute="userform" method="post">
Username:
<form:input path="user.loginName"/>
<!-- More fields -->
<form:select path="user.groups" multiple="true">
<form:options items="${userform.groups}" itemValue="id" itemLabel="name" />
</form:select>
<button type="submit">Create</button>
</form:form>
Any help is appreciated!
Upvotes: 0
Views: 6085
Reputation: 124461
The object gets recreated and values are bound to the resulting object. Which means no group objects.
Also those shouldn't be in the object at all. To solve use a @ModelAttribute
annotated method, which will be invoked for each request handling method and create an object and fill the list of groups.
@ModelAttribute
public void init(Model model) {
UserForm userForm = new UserForm();
userForm.setUser(new User());
model.addAttribute("userform", userForm);
model.addAtrribute("groups", userGroupService.getAllEnabledGroups());
}
@RequestMapping(value = "/admin/users/CreateUser", method = RequestMethod.GET)
public String createUsetGet () {
return "/admin/users/CreateUser";
}
@RequestMapping(value = "/admin/users/CreateUser", method = RequestMethod.POST)
public String createUserPost (@Valid @ModelAttribute("userform") UserForm userForm, BindingResult result) {
if (result.hasErrors() == true) {
return "/admin/users/CreateUser";
}
userForm.getUser().setPassword(passwordEncoder.encode(userForm.getUser().getPassword()));
userGroupService.saveUser(userForm.getUser());
return "redirect:/admin/users/ViewUsers";
}
Ofcourse your jsp has to change slightly also.
<form:select path="user.groups" multiple="true">
<form:options items="${groups}" itemValue="id" itemLabel="name" />
</form:select>
There is one drawback of using this approach now the userGroupService.getAllEnabledGroups()
is called for each incoming request. This might not be needed. You could store those in the session using the @SessionAttributes
annotation on the class.
@Controller
@SessionAttributes("groups")
public class UserController {
@Autowired
UserGroupService userGroupService;
@Autowired
BCryptPasswordEncoder passwordEncoder;
@InitBinder
public void initBinder (WebDataBinder binder) {
binder.registerCustomEditor(Set.class, "userform.user.groups", new GroupListEditor(userGroupService));
}
@ModelAttribute("groups")
public List<Group> groups() {
return userGroupService.getAllEnabledGroups();
}
@ModelAttribute("userform")
public UserForm userform() {
UserForm userForm = new UserForm();
userForm.setUser(new User());
return userForm;
}
@RequestMapping(value = "/admin/users/CreateUser", method = RequestMethod.GET)
public String createUsetGet () {
return "/admin/users/CreateUser";
}
@RequestMapping(value = "/admin/users/CreateUser", method = RequestMethod.POST)
public String createUserPost (@Valid @ModelAttribute("userform") UserForm userForm, BindingResult result, SessionStatus status) {
if (result.hasErrors() == true) {
return "/admin/users/CreateUser";
}
userForm.getUser().setPassword(passwordEncoder.encode(userForm.getUser().getPassword()));
userGroupService.saveUser(userForm.getUser());
status.setComplete();
return "redirect:/admin/users/ViewUsers";
}
}
You will then need, on success, to tell the SessionStatus
that you are finished. If you don't do this your session might pollute.
Upvotes: 2
Reputation: 398
It's because the information about the validation errors is lost after redirect. You can solve this using RedirectAttributes. Check this tutorial.
Upvotes: 1