Hey I've got a problem with updating my object. I've got a table of movies where can i delete it or go to details. In details i wanted to have my "update" option. Im giving whole object to movieDetailPage, it shows object details in input fields but i cant update it. My "updateMovie" method doesnt see what i typed in input. For example im changing name from "Movie1" to "Movie2" and method still see "Movie1"
Here's my movieDetailsPage.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form th:object="${movieToUpdate}" method=post >
<p>Movie name: <input type="text" th:field="*{name}"></p>
<p>Movie description: <input type="text" th:field="*{description}"></p>
<img th:src="${movieToUpdate.getImageUrl()}" th:width="300" th:height="300"><br>
<p>Nowy image url: <input type="text" th:field="*{imageUrl}" class="cloudinary-fileupload"></p>
<a th:href="${'/movie/update/'+movieToUpdate.getId()}">Update</a><br>
</form>
</body>
</html>
and my controller class
package pl.fsaaa.filmapp.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import pl.fsaaa.filmapp.model.Movie;
import pl.fsaaa.filmapp.service.CloudinaryImageUploader;
import pl.fsaaa.filmapp.service.MovieService;
import java.io.IOException;
import java.util.List;
#Controller
public class MovieController {
private CloudinaryImageUploader cloudinaryImageUploader;
private MovieService movieService;
#Autowired
public MovieController(MovieService movieService, CloudinaryImageUploader cloudinaryImageUploader) {
this.movieService = movieService;
this.cloudinaryImageUploader = cloudinaryImageUploader;
}
#GetMapping("/addmovie")
public String getNewMovie(Model model) {
model.addAttribute("newMovie",new Movie());
// model.addAttribute("newImage",new File(""));
return "addMoviePage";
}
#PostMapping("/add-movie")
public String addMovie(#ModelAttribute Movie movie) throws IOException {
cloudinaryImageUploader.saveImageToCloudinary(movie.getImageUrl());
movie.setImageUrl(cloudinaryImageUploader.getCloudinaryImageUrl());
movieService.addMovie(movie);
return "redirect:/addmovie";
}
#GetMapping("/movies")
public String getAllMovies(Model model) {
List<Movie> movieList = movieService.getAllMovies();
model.addAttribute("allMovies",movieList);
return "moviesPage";
}
#GetMapping("/movie/{id}")
public String getMovieDetail(Model model, #PathVariable Long id) {
Movie movieToUpdate = movieService.getMovieById(id);
System.out.println(movieToUpdate);
model.addAttribute("movieToUpdate", movieToUpdate);
return "movieDetailsPage";
}
#RequestMapping(value = "/movie/update/{id}", method = {RequestMethod.GET,RequestMethod.PUT})
// #PutMapping("movie/update/{id}")
public String updateMovie(#PathVariable Long id, #ModelAttribute Movie movieToUpdate){
// movieToUpdate = movieService.getMovieById(id);
// System.out.println(movieToUpdate);
movieService.addMovie(movieToUpdate);
return "redirect:/movies";
}
#RequestMapping(value = "/movie/delete/{id}", method = {RequestMethod.DELETE,RequestMethod.GET})
public String deleteMovie(#PathVariable Long id){
movieService.deleteMovie(id);
return "redirect:/movies";
}
}
Problem solved.
I've added submit button to details page instead of link:
<form th:action="'/movies/update/'+*{id}" th:object="${movieToUpdate}" method=put >
<p>Movie name: <input type="text" th:field="*{name}"></p>
<p>Movie description: <input type="text" th:field="*{description}"></p>
<img th:src="${movieToUpdate.getImageUrl()}" th:width="300" th:height="300"><br>
<p>New image url: <input type="text" th:field="*{imageUrl}" class="cloudinary-fileupload"></p>
<p><input type="submit" value="Update"></p>
</form>
and changed update edited "movie" method a little bit, because previous one wasn't uploading changed image to cloudinary.
#RequestMapping(value = "/update/{id}", method = {RequestMethod.GET,RequestMethod.PUT})
public String updateMovie(#PathVariable Long id, #ModelAttribute Movie movieToUpdate) throws IOException {
movieToUpdate.setId(id);
cloudinaryImageUploader.saveImageToCloudinary(movieToUpdate.getImageUrl());
movieToUpdate.setImageUrl(cloudinaryImageUploader.getCloudinaryImageUrl());
movieService.addMovie(movieToUpdate);
return "redirect:/movies/all";
}
Related
I was following this getting started guide and following error:
org.thymeleaf.exceptions.TemplateProcessingException: Error during execution of processor 'org.thymeleaf.spring5.processor.SpringInputGeneralFieldTagProcessor' (template: "index" - line 10, col 31)"
The code is exactly same as shown in the guide. I understand this is something to do with Thymeleaf form tag, so I also checked the Thymeleaf documentation but couldn't figure out what is wrong with my code.
Here's the code:
index.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Getting Started: Handling Form Submission</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>Form</h1>
<form action="#" th:action="#{/greeting}" th:object="${greeting}" method="post">
<p>Id: <input type="text" th:field="*{id}" /></p>
<p>Message: <input type="text" th:field="*{content}" /></p>
<p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
</form>
</body>
</html>
GreetingController.java
package com.example;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
#Controller
public class GreetingController {
#GetMapping("/greeting")
public String greetingForm(Model model) {
model.addAttribute("greeting", new Greeting());
return "greeting";
}
#PostMapping("/greeting")
public String greetingSubmit(#ModelAttribute Greeting greeting) {
return "result";
}
}
Greeting.java
package com.example;
public class Greeting {
private long id;
private String content;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
I would appreciate any advice.
So tutorial says you to create 2 files, result.html and greeting.html. And you created index.html. The thing is that you didn't bind your template page to Spring controller, it's bound to greeting.html as in tuturial.
So solution is just to change return from greeting to index in greetingForm() method:
#GetMapping("/greeting")
public String greetingForm(Model model) {
model.addAttribute("greeting", new Greeting());
return "index"; // <- changed line
}
Hope this would help you.
I am stuck with an error in a simple Spring Boot application.
My error: Bean property 'menu' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
The problem arises when I click on the "Add Menu" button in the Thymeleaf view.
I searched on the web for possible solutions and even if it may look like a straight forward bug, I don't figure out how to manage it.
I changed and checked several times the Menu class but I don't figure out the problem.
The application is for creating a custom menu with cheeses.
The MenuController:
package com.ghiurutan.springdemo.controllers;
import com.ghiurutan.springdemo.data.CheeseDAO;
import com.ghiurutan.springdemo.data.MenuDAO;
import com.ghiurutan.springdemo.models.Cheese;
import com.ghiurutan.springdemo.models.Menu;
import com.ghiurutan.springdemo.models.forms.AddMenuItemForm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.validation.Valid;
#Controller
#RequestMapping("menu")
public class MenuController {
#Autowired
private MenuDAO menuDAO;
#Autowired
private CheeseDAO cheeseDAO;
#RequestMapping(value = "")
public String index(Model model) {
model.addAttribute("title", "Menus");
model.addAttribute("menus", menuDAO.findAll());
return "menu/index";
}
#RequestMapping(value = "add", method = RequestMethod.GET)
public String add(Model model) {
model.addAttribute("title", "Add Menu");
model.addAttribute(new Menu());
return "menu/add";
}
#RequestMapping(value = "add", method = RequestMethod.POST)
public String add(Model model, #ModelAttribute #Valid Menu menu, Errors errors) {
if (errors.hasErrors()) {
model.addAttribute("title", "Add Menu");
model.addAttribute(new Menu());
return "menu/add";
}
menuDAO.save(menu);
return "redirect:view/" + menu.getId();
}
#RequestMapping(value = "view/{menuId}", method = RequestMethod.GET)
public String viewMenu(Model model, #PathVariable int menuId) {
Menu menu = menuDAO.findOne(menuId);
model.addAttribute("title", menu.getName());
model.addAttribute("cheeses", menu.getCheeses());
model.addAttribute("menuId", menu.getId());
return "menu/view";
}
#RequestMapping(value = "add-item/{menuId}", method = RequestMethod.GET)
public String addItem(Model model, #PathVariable int menuId) {
Menu menu = menuDAO.findOne(menuId);
AddMenuItemForm form = new AddMenuItemForm(cheeseDAO.findAll(), menu);
model.addAttribute("title", "Add item to menu: " + menu.getName());
model.addAttribute("form", form);
return "menu/add-item";
}
#RequestMapping(value = "add-item", method = RequestMethod.POST)
public String addItem(Model model, #ModelAttribute #Valid AddMenuItemForm form, Errors errors) {
if (errors.hasErrors()) {
model.addAttribute("form", form);
model.addAttribute("title", "Add item to menu: " + form.getMenu().getName());
return "menu/add-item";
}
Cheese cheese = cheeseDAO.findOne(form.getCheeseId());
Menu menu = menuDAO.findOne(form.getMenuId());
menu.addItem(cheese);
menuDAO.save(menu);
return "redirect:/menu/view/" + menu.getId();
}
}
The Menu model class:
package com.ghiurutan.springdemo.models;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.List;
#Entity
public class Menu {
#Id
#GeneratedValue
private int id;
#NotNull
#Size(min = 3, max = 15)
private String name;
#ManyToMany
private List<Cheese> cheeses;
public Menu() {
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void addItem(Cheese item) {
cheeses.add(item);
}
public List<Cheese> getCheeses() {
return cheeses;
}
}
The thymeleaf view from where I press the Add Menu button:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
<head th:replace="fragments :: head"></head>
<body class="container">
<h1 th:text="${title}">Default title</h1>
<nav th:replace="fragments :: navigation"></nav>
<ul>
<li th:each="menu : ${menus}">
<a th:href="'/menu/view/' + ${menu.id}" th:text="${menu.name}"></a>
</li>
</ul>
<p>
Add Menu
</p>
</body>
</html>
And the thymeleaf view template that crashes when is trying to render:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
<head th:replace="fragments :: head"></head>
<body class="container">
<h1 th:text="${title}">Default Title</h1>
<nav th:replace="fragments :: navigation"></nav>
<form method="post" style="max-width: 600px" th:object="${menu}">
<div class="form-group">
<label th:for="name">Name</label>
<input class="form-control" th:field="*{menu}"/>
<span th:errors="*{name}" class="error"></span>
</div>
<input type="submit" value="Create Menu"/>
</form>
</body>
</html>
In the web page appears: Error during execution of processor 'org.thymeleaf.spring4.processor.attr.SpringInputGeneralFieldAttrProcessor' (menu/add:12)
The line 12 is: <input class="form-control" th:field="*{menu}"/>
Thanks a lot!
I am trying make hello world using ajax and spring, but it not workit, I dont know why.
My servlet is:
import javax.servlet.http.HttpServlet;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
public class SomeController {
#RequestMapping(value = "/profile", method = RequestMethod.GET)
public #ResponseBody
String processAJAXRequest(
#RequestParam("firstname") String firstname,
#RequestParam("lastname") String lastname) {
String response = "HELLO: " + firstname + " " + lastname;
return response;
}
}
And my page with ajax is:
<html>
<head>
<title>TODO supply a title</title>
<meta charset="windows-1250">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
</head>
<body>
<form id="sampleForm" method="post" action="/profile">
<div>
<input type="text" name="firstname" id="firstname">
</div>
<div>
<input type="text" name="lastname" id="lastname">
</div>
<div>
<button type="submit" name="submit">Submit</button>
</div>
</form>
<script>
$(document).ready(function () {
$('#sampleForm').submit(
function (event) {
var firstname = $('#firstname').val();
var lastname = $('#lastname').val();
var data = 'firstname='
+ encodeURIComponent(firstname)
+ '&lastname='
+ encodeURIComponent(lastname);
$.ajax({
url: $("#sampleForm").attr("action"),
data: data,
type: "GET",
success: function (response) {
alert(response);
},
error: function (xhr, status, error) {
alert(xhr.responseText);
}
});
return false;
});
});
</script>
</body>
</html>
When I try to send form, I am getting error 404. Can you help me? Where is a problem?
Normal servlet extends from HttpServlet working fine.
I have a problem on validating a simple "NumberValidate" Object
Here you see the JSP file:
<%#page contentType="text/html" pageEncoding="UTF-8"%>
<%#taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%#taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
</head>
<body>
<h1>Vul een nummer in:</h1>
<form:form action="form" modelAttribute="number" method="POST">
<form:input path="number"/>
<form:errors path="number"/>
<input type="submit" value="submit"/>
</form:form>
</body>
</html>
Controller:
package controller;
import domain.NumberValidate;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
public class ValidationController {
#RequestMapping(value = {"form"}, method = RequestMethod.GET)
public String showHomePage(Model model){
model.addAttribute("number", new NumberValidate());
return "validation";
}
#RequestMapping(value = {"form"}, method = RequestMethod.POST)
public String showHomePage(#Valid #ModelAttribute NumberValidate number, BindingResult result){
if(result.hasErrors())
return "validation";
return "success";
}
}
The "NumberValidate" Class:
package domain;
import javax.validation.constraints.Min;
public class NumberValidate {
#Min(50)
private int number;
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
When i run the application it starts normal with the textbox etc.
When I type a number less then 40 it gives the error:
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'number' available as request attribute
Can somebody help me with this problem?
In showHomePage method, change to #ModelAttribute("number") and:
if(result.hasErrors()) {
return "validation";
}
return "success";
if validation has error then you need to add number attribute in model when returning to validation view. Code snippet is below :
#RequestMapping(value = {"form"}, method = RequestMethod.POST)
public String showHomePage(#Valid #ModelAttribute NumberValidate number, BindingResult result){
if(result.hasErrors()){
model.addAttribute("number", number);
return "validation";
}
return "success";
}
I have an issue with form processing using Thymeleaf and Spring-MVC.
This is my view:
<html xmlns:th="http://www.thymeleaf.org">
<head>
</head>
<body>
<div class="container" id="containerFragment" th:fragment="containerFragment">
<form
action="#"
th:action="#{/search}"
th:object="${searchInfo}"
method="post" >
<fieldset id="search-query">
<input
type="text"
name="search"
value=""
id="search"
placeholder="Search for user"
required="required"
th:value="*{searchQuery}" />
<input
type="submit"
value="Search"
name="submit"
class="submit"/>
</fieldset>
</form>
</div>
</body>
</html>
this is my controller:
/** Search form */
#RequestMapping(value = "/search", method = RequestMethod.GET)
public String search(Model model) {
model.addAttribute("searchInfo", new SearchForm());
return "search";
}
/** Search form */
#RequestMapping(value = "/search", method = RequestMethod.POST)
public ModelAndView search(BindingResult result,
#Valid #ModelAttribute("searchInfo") SearchForm searchForm) {
String login = searchForm.getSearchQuery();
User user = userService.findUserByLogin(login);
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("search-results");
modelAndView.addObject("user", user);
return modelAndView;
}
and the search form is:
public class SearchForm {
String searchQuery;
public String getSearchQuery() {
return searchQuery;
}
public void setSearchQuery(String searchQuery) {
this.searchQuery = searchQuery;
}
#Override
public String toString() {
return "SearchForm [searchQuery=" + searchQuery + "]";
}
}
The issue is that login is null at this point of controller:
String login = searchForm.getSearchQuery();
It looks like a new SearchForm object created for POST method, but there are already one, which was created at GET step and should contains the search query.
I can't understand such behaviour.
Spring should map HTML form attributes to your model: SearchForm.
Spring MVC build accordions with request parameters and your model object properties and set matching properties into your model Object before pass object into your controller method.
You named HTML property(and request parameter name automatically) as id="search". But SearchForm hasn't such property. Instead it has searchQuery property. So after Spring MVC unable to set searchQuery value into your SearchForm it pass model with null attribute.
Please Change th:value="{searchQuery}" to th:field="{searchQuery}".
I hope it'll work.
It worked for me:
FormTestController.java
#Controller
public class FormTestController {
#RequestMapping(value = "/form-test-1.jhtml", method = RequestMethod.GET)
public String formTest1(#ModelAttribute("form1") Form1TestVO form1TestVO, Model model){
System.out.println("You've submited: " + form1TestVO.getName())
model.addAttribute("form1", new Form1TestVO("Form 1 test"));
return "form-test-1";
}
}
form-test-1.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.thymeleaf.org" >
<head>
<title>Form test 1</title>
</head>
<body >
<form th:object="${form1}" th:action="#{/form-test-1.jhtml}" >
<input th:field="*{name}" />
<button>Send</button>
</form>
</body>
</html>
Form1TestVO
public class Form1TestVO {
private String name;
public Form1TestVO() {
}
public Form1TestVO(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Reference