Thymeleaf how to use model atrribute correctly its throwing null error - spring-boot

public class Customer {
#Id
#GeneratedValue
private Integer id;
private String email;
private Long Apprvamount;
#JsonIgnore
#OneToMany(mappedBy="customer", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<MyTransaction> transactions;
#Transient
private Long rewardPoints;
public Long getRewardPoints() {
function to calculate rewardPoints;
}
Contoller adding model attribute
#GetMapping("/customers/{id}")
public String getCustomer(#PathVariable Integer id,Model model) throws RecordNotFoundException {
Customer customer = rewardsService.getCustomerById(id);
model.addAttribute("customer", customer);
return "profile";
}
Thymeleaf
<body>
<div class="card-body">
<h2 th:text="${customer.getEmail()}"></h2>
<p th:text="${customer.getApprvamount()}"></p>
<p th:text="${customer.getRewardPoints()}"></p>
<p class="my-5">
<a href="/{id}(id=${customer.getId()})/emi" class="btn btn-primary">
<i> All</i></a>
</p>
Its sometime throwing null error for getRewardPoints its working for email and approvamount.And url is not getting calculated.Getters and setters are present.What is wrong with the approach?

You may have misspelt customer:
model.addAttribute("costumer", customer);

Related

Thymeleaf form returns null values

when I submit the form, the pageBook.id and loggedUser.id values become null (in GetMapping method they have values). Any ideas why?
This is my form:
<div sec:authorize="isAuthenticated()">
<form th:action="#{/} + ${pageBook.id}" th:object="${transaction}" method="post">
<input type="hidden" th:field="${transaction.bookTransaction}" th:value="${pageBook.id}">
<input type="hidden" th:field="${transaction.userTransaction}" th:value="${loggedUser.id}">
<input type="submit" value="Submit!" />
</form>
</div>
My Entity:
#Entity
#Table(name = "transactions")
public class Transaction {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="book_id", nullable=false)
private Book bookTransaction;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="user_id", nullable=false)
private User userTransaction;
... consturctors getters and setters
and my controller (pageBook and loggedUser aren't null):
#GetMapping("/{id}")
public String showBookPage(#AuthenticationPrincipal UserDetails userDetails, #PathVariable("id") Long id, Model model, RedirectAttributes redirectAttributes){
try {
Book pageBook = bookService.getBookById(id);
User loggedUser = (User) userService.loadUserByUsername(userDetails.getUsername());
model.addAttribute("transaction", new Transaction());
model.addAttribute("pageBook", pageBook);
model.addAttribute("loggedUser", loggedUser);
} catch (BookNoFoundException e) {
redirectAttributes.addFlashAttribute("message", e);
}
return "users/users_book_page";
}
#PostMapping("/{id}")
public String newTransaction(#PathVariable("id") Long id, #ModelAttribute("transaction") Transaction transaction){
log.info(transaction.getUserTransaction());
log.info(transaction.getBookTransaction());
transactionService.newTransaction(transaction);
return "redirect:/" + id;
}

Thymeleaf : error while parsing user credentials inside registration form Springboot

I need to complete the registration form on SpringBoot. To achieve this I created 2 classes : User and Credentials, the 2nd handles unique username and password.
#Getter
#Setter
#Entity
#Table(name = "users")
public class User {
public User() {}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String username;
}
public class Credentials {
public static final String DEFAULT_ROLE = "DEFAULT";
public static final String ADMIN_ROLE = "ADMIN";
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Getter
#Column(nullable = false, unique = true)
private String username;
#Getter
#Column(nullable = false)
private String password;
#Getter
#Setter
#Column(nullable = false)
private String role;
#OneToOne(cascade = CascadeType.ALL)
private User user;
}
This is registrationController (handles "/registration" requests) :
#Controller
public class RegController {
#GetMapping("/register")
public String register(Model model){
model.addAttribute("user", new User());
model.addAttribute("credentials", new Credentials());
return "register";
}
}
Now it should be all set, ready to read data from html form and register new users using thymeleaf.
This is registration form inside registration.html, my goal is to read username and password then create a new user and save him on Postgres :
<body>
<div class="login">
<form id="login" method="POST" th:action="#{/register}">
<label><b>User Name
</b>
</label>
<input type="text" name="Uname" id="Uname" placeholder="Username" required th:field="${credentials.username}">
<br><br>
<label><b>Password
</b>
</label>
<input type="Password" name="Pass" id="Pass" placeholder="Password" required th:field="${credentials.password}">
<br><br>
<label><b>Repeat Password
</b>
</label>
<input type="Password" name="RPass" id="RPass" placeholder="Password">
<br><br>
<button type="submit" class="btn btn-primary" id="log">Registrati</button>
<div style = "background-color:green">
<a id= "link" th:href="#{/login}" >or login</a>
</div>
</form>
</div>
</body>
But when I navigate to registration.html this is the error from TomCat :
Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException:
An error happened during template parsing (template: "class path resource [templates/register.html]")
What I noticed is that if I remove all the th:field (used to get user input) the page is shown without error.
I noticed that your Credentials class has only getters for username and password. If Thymeleaf has to fill them with the user input, shouldn't you provide setters too?
#Getter
#Setter
#Column(nullable = false, unique = true)
private String username;
#Getter
#Setter
#Column(nullable = false)
private String password;

How to create confirmation dialog when error occurs

I would like to implement following functionality: If user write "wrong" PID, alert should show up saying: Do you really save this PID with invalid format? If user say yes, the whole "playerform" (if other fields are correct) will be saved to the database (eventhough that PID would be in incorrect format.)
So how can I achieve this? I would add #Patern(with right regex) and message - this message will be displayed to the user because of the template down below, but how can I add this kind of dialog (yes - save the form with invalid PID, no - do not save it).
Spring boot and thymeleaf are used.
Method in the controller
#PostMapping("/create")
public String createPlayer(#Valid #ModelAttribute("playerForm") PlayerForm playerForm,
BindingResult bindingResult,
Model model,
RedirectAttributes redirectAttributes) {
// Check for errors
if (bindingResult.hasErrors()) {
formViewPreparer.prepare(model, new PlayerFormViewPreparer.ParamObj(true, null));
return "admin/players/create";
}
// Prepare object for service layer
CreatePlayerIn createPlayerIn = CreatePlayerInMapper.fromPlayerForm(playerForm);
// Call service layer
playerService.createPlayer(createPlayerIn);
redirectAttributes.addFlashAttribute("message", "player.toast.created");
return "redirect:/admin/players";
}
Player form class
#Getter
#Setter
public class PlayerForm {
#NotBlank
private String firstName;
#NotBlank
private String lastName;
#NotNull
private GenericState playerState;
#DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate birthDate;
private String PID;
private Long schoolId;
private Long teamId;
private String street;
private String city;
private String zipCode;
private String phoneNumber;
private String email;
private String motherPhoneNumber;
private String motherEmail;
private String fatherPhoneNumber;
private String fatherEmail;
#DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate validityOfMedicalExam;
#NumberFormat(style = NumberFormat.Style.NUMBER)
#Min(0)
private Integer annualFee;
private String annualFeeNote;
#NumberFormat(style = NumberFormat.Style.NUMBER)
#Min(0)
private Integer firstSemiAnnualFee;
private String firstSemiAnnualFeeNote;
#NumberFormat(style = NumberFormat.Style.NUMBER)
#Min(0)
private Integer secondSemiAnnualFee;
private String secondSemiAnnualFeeNote;
create.html template utilizing thymeleaf
<form th:object="${playerForm}"
th:action="#{/admin/players/create}"
novalidate
enctype="multipart/form-data"
method="post">
<div class="form-group col-md-6">
<input type="text" th:field="*{PID}" class="form-control" th:errorclass="is-invalid"
th:placeholder="#{player.input.PID}"/>
<label for="PID" th:text="#{player.input.PID}" class="form-control-placeholder"></label>
<div th:if="${#fields.hasErrors('PID')}" class="invalid-feedback"
th:errors="*{PID}"></div>
</div>

Spring MVC Error: Failed to convert property value of type java.lang.String to required type

I can't let this exception go:
Failed to convert property value of type java.lang.String to required type com.company.springdemo.entity.Product for property productId; nested exception is java.lang.IllegalStateException: Cannot convert value of type java.lang.String to required type com.company.springdemo.entity.Product for property productId: no matching editors or conversion strategy found
Order Model
#Entity
#Table(name = "orders") // naming the table only order, will throw exception
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "order_id")
private Integer orderId;
#OneToOne(cascade = {CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH})
#JoinColumn(name = "product_id")
private Product productId;
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH})
#JoinColumn(name = "client_id")
private Client client;
....
Product Model
#Entity
#Table(name = "product")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "product_id")
private Integer id;
#Column(name = "product_name")
private String productName;
#Column(name = "product_serial")
private String productSerial;
...
Client Model
#Entity
#Table(name = "clients")
public class Client {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#NotEmpty
#Column(name = "first_name")
private String firstName;
#NotEmpty
#Column(name = "last_name")
private String lastName;
#NotEmpty
#Email
#Column(name = "email")
private String email;
#NotEmpty
#Column(name = "location")
private String location;
#OneToMany(mappedBy = "client",cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Order> orders;
Controller, where I save the order with related client and product
#PostMapping("add")
public ModelAndView addOrder( #Validated #ModelAttribute("ords") Order order, BindingResult bindingResult ){
if (bindingResult.hasErrors()) {
System.out.println("Having errors: " + bindingResult.getAllErrors());
Iterable<Product> products = productService.listProducts();
Iterable<Client> clients = clientService.listClients();
System.out.println("Error "+ bindingResult.getAllErrors());
ModelAndView mv = new ModelAndView("orders/add-order");
mv.addObject("products",products);
mv.addObject("clients",clients);
return mv;
}
try {
orderService.saveOrder(order);
} catch (Exception e) {
e.printStackTrace();
}
ModelAndView mv = new ModelAndView("redirect:list");
return mv;
}
Finally, my JSP form View page
<form:form action="add" method="post" modelAttribute="ords">
<label for="productId" >Product Id</label>
<form:select path="productId" >
<c:forEach var="product" items="${products}">
<form:option value="${product.id}">${product.productName}</form:option>
</c:forEach>
</form:select>
<form:errors path="productId"/>
<br>
<label for="client" >Client Id</label>
<form:select path="client" >
<c:forEach var="client" items="${clients}">
<form:option value="${client.id}">${client.id} - ${client.lastName}</form:option>
</c:forEach>
</form:select>
<form:errors path="client"/>
<br>
<input type="submit" value="Place Order">
</form:form>
What am I doing wrong?
You most likely need to build a converter class such as this one :
#Component("facilityConverter")
public class FacilityConverter implements Converter<String, Facility>
{
#Autowired
FacilityService facilityService;
#Override
public Facility convert(String id)
{
return facilityService.findById(Integer.parseInt(id));
}
}
Then, you need to register it by implementing the addFormatters method inside of a configuration class implementing WebMvcConfigurer like so :
#Override
public void addFormatters (FormatterRegistry registry)
{
registry.addConverter((FacilityConverter)ctx.getBean("facilityConverter"));
}
Your entities will then correctly be mapped from a dropdown selection. Also, this might not be part of your issue but you can just build your dropdowns like this :
<form:select name="linkedInterface" path="linkedInterface" id="linkedInterface">
<form:options items="${interfaces}" itemLabel="name" itemValue="id"/>
</form:select>
The productId field is actually a Product object, not an ID (String/int). You need your JSP to use path="productId.id" rather than path="productId".
(Although I'd also suggest you also rename the field product rather than productId.)
<form:select path="product.id">
I think you'll hit the same issue on your <form:select path="client"> too.

Thymeleaf Spring Boot: Binding form to object,some fields are empty?

So I am binding a form to an object to send to a spring controller,the "chosenAdvertisementsIds" being set with a hidden tag of ${advertisement.id} ends up with blank strings,the correct quantity of strings but empty strings.The tag for advertisement.description prints correctly on the page so the content is in the model correctly.
public class EditScheduleForm {
private String[] chosenAdvertisementsIds;
private String[] chosenAdvertisementsTime;//parralel arrays
private String dates;
//getters setters
}
My html,
<form th:action="#{|/web/editSchedule/${schedule.id}|}"
th:object="${EditScheduleForm}" method="post">
<h1>Advertisement Items</h1>
<div th:each="advertisement : ${chosenAdvertisements}">
<p th:text="${advertisement.description}"></p>
<input type="hidden" th:value="${advertisement.id}" th:field="*{chosenAdvertisementsIds}"/>
<p>Type below what times you want this advertisement to play at(type it like this 10:15/11:15/14:15 )</p>
<input name="text" th:field="*{chosenAdvertisementsTime}" type="text"/>
My controller method
#RequestMapping(value="/web/editSchedule/{scheduleId}",method = RequestMethod.POST)
public String editScheduleFormPost(Model model,
#ModelAttribute EditScheduleForm editScheduleForm,
#PathVariable Long scheduleId,
RedirectAttributes redirectAttributes) {
Advertisement
#Entity
public class Advertisement {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "usergroup_id")
private UserGroup userGroup;
private String description;
#Basic
#Temporal(TemporalType.DATE)
private java.util.Date dateAdded;
//getters setters
}
How I populate the model that the html page is on
#RequestMapping(value="/web/editschedule/{scheduleId}",method = RequestMethod.GET)
public String editOneSchedule(#PathVariable Long scheduleId,
Model model) {
// Get the schedult given by Id
Schedule schedule = scheduleService.findOne(scheduleId);
model.addAttribute("schedule",schedule);
ArrayList<Music> chosenMusics = new ArrayList<>();
for(int i=0;i<schedule.getMusicScheduleItems().size();i++){
chosenMusics.add(schedule.getMusicScheduleItems().get(i).getMusic());
}
model.addAttribute("chosenMusics",chosenMusics);
ArrayList<Advertisement> chosenAdvertisements = new ArrayList<>();
for(int i=0;i<schedule.getAdvertisementScheduleItems().size();i++){
chosenAdvertisements.add(schedule.getAdvertisementScheduleItems().get(i).getAdvertisement());
}
model.addAttribute("chosenAdvertisements",chosenAdvertisements);
model.addAttribute("EditScheduleForm", new EditScheduleForm());
return "editschedule";
}

Resources