Thymeleaf cannot find declaration - java

I have a problem with thymeleaf. It can not find newUser model in the html file.
<form th:action="#{/users/add}" th:object="${newUser}" method="post">
<p>User Name: <input type="text" th:field="*{userName}"></p>
<p>Email: <input type="text" th:field="*{email}"></p>
<p>Password: <input type="text" th:field="*{password}"></p>
<p>Role:
<select th:field="*{role}">
<option th:each="role: ${roles}" th:value="${role.getId()}" th:utext="${role.getUserRole()}">
</option>
</select>
</p>
<p><input type="submit" value="Add User"></p>
</form>
From this class:
package Application.Controllers;
#Controller
#RequestMapping("/")
public class TemplateController {
private final RoleService roleService;
#Autowired
public TemplateController(RoleService roleService) {
this.roleService = roleService;
}
#GetMapping("register")
public String registerUser(Model model){
model.addAttribute("roles", roleService.listRoles());
model.addAttribute("newUser", new Users());
return "redirect:login";
}
}
The problem is that from other class and html it works fine.
Could you tell me what is wrong?

In your LoginUser make sure to add this line:
model.addAttribute("roles", roleService.listRoles());
model.addAttribute("newUser", new Users());
Or change the registerUser return just the view don't redirect to another action:
#GetMapping("register")
public String registerUser(Model model){
model.addAttribute("roles", roleService.listRoles());
model.addAttribute("newUser", new Users());
return "login";
}

Related

ThymeLeaf: Neither BindingResult nor plain target object for bean name available as request attribute

I am trying to build a simple select and Option list in thymeleaf, but i keep getting the error
Neither BindingResult nor plain target object for bean name 'BOLForm' available as request attribute
I am pretty sure there is something wrong with my bol.html. But not able to figure out the missing mappings.
Below is my Controller:
#Controller
public class BillOfLadingController {
#Autowired
private DepotList depotList;
#GetMapping("/BillOfLading")
public String getBOLForm(){
return "bol";
}
#PostMapping("/BillOfLading")
public String Login(#ModelAttribute(name="BOLForm") BOLForm bolForm,Model model) {
model.addAttribute("BOLForm", bolForm);
List<DepotDetailEntity> depotDropDown = depotList.getDepots().getDepotDetail();
if(depotDropDown.size() == 0) {
return "login";
}
model.addAttribute("depots", depotDropDown);
return "bol";
}
}
The Form Class
#Component
public class BOLForm {
private String BOL;
private String depotId;
public String getBOL() {
return BOL;
}
public void setBOL(String bOL) {
BOL = bOL;
}
public String getDepotId() {
return depotId;
}
public void setDepotId(String depotId) {
this.depotId = depotId;
}
}
the bol.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>BillOfLading Request Page</title>
</head>
<body>
<h1>BillOfLading Report</h1>
<form th:action="#{/BillOfLading}" th:object="${BOLForm}" method="post">
<label for="bol">BOL No.</label>
<input type="text" id="bol" name="bol">
<br/>
<table>
<tr>
<td>Select DC:</td>
<td>
<select th:field="*{depotId}">
<option value=""> -- </option>
<option th:each="depot : ${depots}"
th:value="${depot.depotId}"
th:utext="${depot.depotName}"/>
</select>
</td>
</tr>
<tr>
<td><input name="submit" type="submit" value="submit" /></td>
</tr>
</table>
</form>
</body>
</html>
When I replace my bol.html as below, it works:
<form th:action="#{/BillOfLading}" th:object="${BOLForm}" method="post">
<label for="bol">BOL No.</label>
<input type="text" id="bol" name="bol">
<br/>
<label for="depotId">DepotID </label>
<input type="text" id="depotId" name="depotId">
<br/>
In the controller, you need to add the BOLForm object as an attribute of the model:
#GetMapping("/BillOfLading")
public String getBOLForm(Model model){
model.addAttribute("BOLForm", new BOLForm());
return "bol";
}

Upload files to the #ModelAttribute using Thymeleaf

How to upload files to the #ModelAttribute using Thymeleaf?
I'am doing something that:
upload.html
<form method="POST" action="#" th:action="#{/sending}" th:object="${collage}" enctype="multipart/form-data" >
<input type="file" th:field="*{picture}" />
<input type="file" th:field="*{picture}" />
<input type="submit" value="upload" />
</form>
My controller:
#Controller
public class MainController {
#GetMapping(value = { "/" })
public String index(){
return "upload";
}
#GetMapping("/collage")
public String paintPicture(Model model){
return "collage";
}
#PostMapping("/sending")
public String redirect(#ModelAttribute(value="collage") Collage collage, RedirectAttributes redirectAttr) {
Collections.shuffle(Arrays.asList(collage.getCollage()));
redirectAttr.addFlashAttribute("pictures",collage.getCollage());
return "redirect:/collage";
}
}
Collage.class:
public class Collage {
private MultipartFile[] pictures;
public Collage(){}
public MultipartFile[] getCollage() {
return pictures;
}
public void setCollage(MultipartFile[] pictures) {
this.pictures = pictures;
}
}
I'm getting: java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'collage' available as request attribute in the console and a text on "/" page:
One picture is better than 1000 words:
Now code example to upload an array of multipartfiles inside of Entity:
<form action="#" th:action="#{/distribution/save}" class="form-horizontal"
role="form" method="post" th:object="${news}" enctype="multipart/form-data">
<input type="hidden" name="id" value="id" th:field="*{id}"> <div class="form-group has-label"> <label for="inputTitle" th:text="#{news.title}">Título</label>
<input type="text" class="form-control" id="inputTitle" th:placeholder="#{news.title}" th:field="*{title}"></div>
<input type="file" name = "multipartFilesDocument" value="multipartFilesDocument" th:field="*{multipartFilesDocument}" multiple="multiple"/>
<button type="submit" class="btn btn-default"><span th:text="#{common.save}"></span></button>
</div>
</form>
Controller Code:
#PostMapping("/save")
public String saveMultiparthFile(Model model,#ModelAttribute NewsDTO eventDTO){
eventDTO.getId();
return getrDetail(model);
}
Entity code:
public class NewsDTO {
private List<MultipartFile> multipartFilesDocument;
public List<MultipartFile> getMultipartFilesDocument() {
return multipartFilesDocument;
}
public void setMultipartFilesDocument(List<MultipartFile> multipartFilesDocument) {
this.multipartFilesDocument = multipartFilesDocument;
}
}
In this code is really important enctype="multipart/form-data" and name = "multipartFilesDocument" value="multipartFilesDocument" in form
you can apply this changes
1) change #ModelAttibute to #RequestParam
2) use MultipartFile[] as param and only use a single input file html
//name of input html should be collage
#PostMapping("/sending")
public String redirect(#RequestParam("collage") MultipartFile[] files, RedirectAttributes redirectAttr) {
Collections.shuffle(Arrays.asList(files));
redirectAttr.addFlashAttribute("pictures",files);
return "redirect:/collage";
}
and your html page
<form method="POST" th:action="#{/sending}" enctype="multipart/form-data" >
<input type="file" name="collage" multiple="multiple"/>
<input type="submit" value="upload" />
</form>

When using Spring, When is the Form class property stored in the value that's sent from the html form tag?

I should implement like execute(#ModelAttribute RegisterForm registerForm, Model model) in Controller class below.
I forgot put the #ModelAttribute.
However it works. When is name in RegisterForm class stored in the value that's sent from html?
Part of Controller class
#Controller
public class RegisterController {
#RequestMapping(value = "/register/")
public String register() {
return "register";
}
#RequestMapping(value="/register/execute", method=RequestMethod.POST)
//(#ModelAttribute RegisterForm registerForm, Model model) is better
public String execute(RegisterForm registerForm, Model model) {
model.addAttribute("registered_name", registerForm.getName());
return "index";
}
}
Form class
public class RegisterForm {
private String name; //when is this property stored in the value??
public String getName() {
return name;
}
}
Part of HTML file
<div class="input">
<p>Enter your name</p>
<input type="text" name="name" />
</div>
Please let me know if you need more information to solve this.Thanks.
add setter name to your class RegisterForm it's important
you should lik this :
<form action="register/execute">
<div class="input">
<p>Enter your name</p>
<input type="text" name="name" />
</div>
<form>
or usign tag form spring :
add this in your controller
#RequestMapping(value="/index", method=RequestMethod.POST)
public String executet(Model model) {
model.addAttribute("registerForm ", new RegisterForm ());
return "index";
}
include this code in your jsp
<%# taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<form:form method="POST" modelAttribute="registerForm ">
<form:input path="name" />
<input type="submit" value="Register">
</form:form>

How to using th:if tag to check database field using thymeleaf

i am new to spEL and i want to use the "SELECT COUNT(*) FROM Exams;" query with JPQL or spEL to check if the record in the database is less than 6(six);
This is what i have. I can not really figure out how to go about it because i am not able to use the #Query annotation in the controller.
//Problem writing spEL
<div class="form-group" th:if="${exams.count(*) < 6}" >
<form method="post" th:object="${newExam}" th:action="#{/exams}" class="inline new-item">
<label class="form-control input-sm"><input type="text" th:field="*{indexNumber}" placeholder="Index Number" autocomplete="off"/></label>
<select th:field="*{grade}" class="form-control input-sm">
<option value="" disabled="disabled">[Select Grade]</option>
<option th:each="grade : ${grades}" th:value="${grade.values}" th:text="${grade.name}">Grade</option>
</select>
<label class="form-control input-sm"><input type="text" th:field="*{courseOffered}" placeholder="CourseOffered" autocomplete="off"/></label>
<label class="form-control input-sm"><input type="text" th:field="*{examType}" placeholder="ExamType" autocomplete="off"/></label>
<label class="form-control input-sm"><input type="text" th:field="*{subject}" placeholder="Subject" autocomplete="off"/></label>
<label class="form-control input-sm datepicker"><input type="text" th:field="*{gradeYear}" placeholder="ExamYear" autocomplete="off"/></label>
<button type="submit" class="btn btn-primary">Add</button>
</form>
#Controller
public class ExamsController {
#Autowired
private ExamService examService;
#Autowired
private UserService userService;
#RequestMapping(path = "/cert_prog")
public String examsList(Model model){
Iterable<Exams> exams = examService.findAll();
model.addAttribute("exams", exams);
model.addAttribute("newExam", new Exams());
model.addAttribute("grades", Grade.values());
return "cert_prog";
}
#RequestMapping(path = "/mark", method = RequestMethod.POST)
public String toggleComplete(#RequestParam Long id) {
Exams exams = examService.findOne(id);
examService.toggleComplete(id);
return "redirect:/cert_prog";
}
#RequestMapping(path = "/exams", method = RequestMethod.POST)
public String addTask(#ModelAttribute Exams exams, Principal principal){
//User user = userService.findByUsername(principal.getName());
User user = (User)((UsernamePasswordAuthenticationToken)principal).getPrincipal();
exams.setUser(user);
//examService.increment(exams);
examService.save(exams);
return "redirect:/cert_prog";
}
}
#Service
public class ExamServiceImpl implements ExamService {
#Autowired
private ExamsDao examsDao;
#Override
public Iterable<Exams> findAll() {
return examsDao.findAll();
}
#Override
public Exams findOne(Long id) {
return examsDao.findOne(id);
}
#Override
public void toggleComplete(Long id) {
Exams exams = examsDao.findOne(id);
exams.setComplete(!exams.isComplete());
examsDao.save(exams);
}
#Override
public void save(Exams exams) {
examsDao.save(exams);
}
#Override
public void increment(Exams exams) {
//exams.setCounter(exams.getCounter() + 1);
}
You don't need to query the DB again. Exams is already an iterable collection of all exam records, isn't it? Try
<div class="form-group" th:if="${exams.size() < 6}" >
As long as the underlying class of the Iterable object implements the size() method, thymeleaf should be able to find and call it. Otherwise you'll need to cast to a collection that does implement size before you add the object to the model.

Edit form validation with Spring MVC

I use Spring MVC and Spring form validation in my project.
There is class named Group in object model and I created form for editing it.
Form
<spring:url var="saveGroup" value="/teacher/groups/save"/>
<form:form action="${saveGroup}" method="post" modelAttribute="group">
<form:hidden path="id"/>
<div id="nameDiv" class="control-group">
<form:label path="title">Title:</form:label>
<form:input path="title"/>
<form:errors path="title"/>
</div>
<div id="specDiv" class="control-group">
<form:label path="title">Specialty:</form:label>
<form:select path="specialty">
<form:options items="${specialties}" itemValue="id" itemLabel="title"/>
</form:select>
</div>
<div class="center">
<spring:url var="groups" value="/teacher/groups"/>
<input class="btn btn-primary" type="submit" value="Save"/>
<a class="btn" href="${groups}"> Cancel </a>
</div>
</form:form>
Controller
#Controller
#RequestMapping("/teacher/groups")
public class GroupsController {
#Autowired
private GroupService groupService;
#Autowired
private SpecialtyService specialtyService;
#ModelAttribute("group")
public Group setGroup(Long id) {
if (id != null) {
return groupService.read(id);
} else {
return new Group();
}
}
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Specialty.class, "specialty",
new SpecialtyEditor(specialtyService));
binder.setValidator(new GroupValidator());
}
#RequestMapping("")
public ModelAndView groups() {
return new ModelAndView("teacher/groups/list", "groups",
groupService.list());
}
#RequestMapping("/edit")
public ModelAndView editGroup() {
return new ModelAndView("teacher/groups/edit", "specialties",
specialtyService.list());
}
#RequestMapping(value = "/save", method = RequestMethod.POST)
public String saveGroup(#Valid Group group, BindingResult result) {
if (result.hasErrors()) {
return "forward:/teacher/groups/edit";
}
groupService.update(group);
return "redirect:/teacher/groups";
}
}
I want to set the adequate behavior of my form in the case of failed validation. I.e. it should save its state but only show validation error message (as when using javascript to validate).
I thought that "forward:/teacher/groups/edit" will again forward request to editGroup() with saved objects group and result. But when I failed validation form just reloads and show start state of edited group: no errors and no saved changes.
How can I do that correctly?
Thanks!
I solved problem by not forwarding request to other method but sending answer to user immediately. Now it works and looks like:
#RequestMapping(value = "/save", method = RequestMethod.POST)
public ModelAndView saveGroup(#Valid Group group, BindingResult result) {
if (result.hasErrors()) {
return new ModelAndView("/teacher/groups/edit", "specialties", specialtyService.list());
}
groupService.update(group);
return new ModelAndView("redirect:/teacher/groups");
}

Categories