Can pass models to Spring, but Thymeleaf still indicates an error - spring

My problem is that I can pass models between Thymeleaf and Spring, but Thymeleaf still indicates an error.
Spring code:
#GetMapping("{id}/edit")
String getEdit(#PathVariable Long id, Model model) {
postRepository.findById(id).ifPresent(o -> model.addAttribute("post", o));
return "edit";
}
#PostMapping("{id}/edit")
String postEdit(#ModelAttribute Post post) {
postRepository.save(post);
return "redirect:/";
}
Thymeleaf code:
<form th:action="|/${post.id}/edit|" th:method="POST" th:object="${post}">
<input type="text" th:value="*{title}" name="title">
<input type="text" th:value="*{content}" name="content">
<input type="submit" value="Edit">
</form>
Thymeleaf indicates that it can't resolve ${post.id}, *{title} and *{content}. I have stopped and rerun the application more times so I suppose something is amiss in my code, even if it works.
What should I do to solve this issue?

First of all I think you don't need path variable in post mapping. You can use post mapping without path variable. So try modifying you controller like
#PostMapping("/edit")
String postEdit(#ModelAttribute Post post) {
postRepository.save(post);
return "redirect:/";
}
If you write controller like this it will be easy defining path in thymeleaf.
And second error can't resolve *{title} and *{content} is because of invalid keyword. Please try modifying your thymeleaf like
<form th:action="#{/edit}" th:method="POST" th:object="${post}">
<input type="text" th:field="*{title}" name="title">
<input type="text" th:field="*{content}" name="content">
<input type="submit" value="Edit">
</form>
I think this will work as you are expecting.

Related

Spring Boot - Edit data

I did the get, post and delete method. But now I'm suffering to do the put method.
My controller:
#RequestMapping("/teste/equipe/editar/{id}")
public String update(#RequestBody Team newTeam ,#PathVariable("id") Long id)
{
Team team = teamService.findById(id);
team.setName(newTeam.getName());
team.setName(newTeam.getRole());
teamService.save(team);
return "redirect:/teste/equipe";
}
HTML:
<form action="/teste/equipe/editar/{id}" method="PUT">
<input type="text" th:field="${team.name}" placeholder="Nome">
<br>
<input type="text" th:field="${team.role}" placeholder="Função">
<button>
Enviar
</button>
</form>
Error:
Not certain this helps but the error on the whitelabel error page states:
Number Format Exception for input {id}
Seems like you're expecting {id} in your form:
<form action="/teste/equipe/editar/****{id}****" method="PUT">
to be interpolated with an actual ID. Should it instead be:
<form action="/teste/equipe/editar/${id}" method="PUT">

Thymeleaf th:object does not work with controller?

I am new to Thymeleaf. I am trying to create a search form but it doesn't work. But when I tried manually entering localhost:8080/searchMovies/name and it works fine, so whats wrong with my code?
MovieController.java
#GetMapping("/searchMovies/{name}")
public ModelAndView searchMoviesByNameLike(#PathVariable("name") String name) {
List<Movie> searchMovies = movieService.findMovieByNameContaining(name);
ModelAndView modelAndView = new ModelAndView("searchMovies");
modelAndView.addObject("searchMovies", searchMovies);
modelAndView.addObject("searchMoviesList", movieService.findMovieByNameContaining(name));
return modelAndView;
}
header.html
<form th:object="${searchMovies}" th:action="#{/searchMovies}" method="get" class="form-inline my-2 my-lg-0">
<input class="form-control mr-sm-2" type="text" placeholder="" aria-label="Search" th:value="${name}">
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
</form>
th:object (generally) isn't used for get requests. You use th:object when you're submitting (w/POST) a form, and you want Spring to populate the properties of a Java object with the fields on that form.
Since Thymeleaf is a server side processed language, and normal html doesn't support building/forwarding to the kind of url you want you're going to have to use JavaScript to accomplish what you want.
For example, a normal GET request when submitted through a form looks like this:
/searchMovies?property1=value1&property2=value2
if you want your url to look like this:
/searchMovies/value1
then you need to use JavaScript to build that url when the user clicks the button and forward to that url.
you need a name at the end of url but in you didn't set it. in this case you can't set it. you must do it by a different approach. another thing is that you need an object in your form not a list. try the following code :
#GetMapping("/searchMovies")
public String sendSearchForm(Model model) {
model.addAttribute("movie", new Movie());
return "search";
}
<form th:object="${movie}" th:action="#{/searchMovies}" method="post" class="form-inline my-2 my-lg-0">
<input class="form-control mr-sm-2" type="text" placeholder="" aria-label="Search" th:field="*{name}">
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
</form>
#PostMapping("/searchMovie")
public String searchAccounts(#ModelAttribute Movie movie) {
List<Movie> searchMovies = movieService.findMovieByNameContaining(movie.getName());
.
.
.
return modelAndView;

Create urls using forms in Thymeleaf

I am new to Thymeleaf, and I have an issue with dynamically creating URLs using forms.
I have a simple spring boot app that stores objects in a database, and I can query the database using simple urls. For instance, the url /distance/0.3 would return objects whose distance attribute is lower than or equal to 0.3. These urls work perfectly when I edit them in the browser.
I would like users to be able to set these search values themselves. I tried to create a simple form to create the above url with user inputs, but I am getting the following error:
Neither BindingResult nor plain target object for bean name 'dist' available as request attribute
I have tried with this in the html document:
<form th:action="#{/distance/{pathParam}(pathParam=${dist}}">`
<p>Distance: <input type="text" th:field="*{dist}" /></p>
<p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
</form>
and trying various answers from this discussion, but with no luck.
I have also tried to use the controller as suggested here, with this in the controller:
#GetMapping("/distance/search/")
public String userSetDistance(#RequestParam("dist") String dist) {
return "redirect:/distance/" + dist;
}
and this in the html file:
<form th:action="#{/distance/search/}">
<p>Distance: <input type="text" th:field="*{dist}" /></p>
<p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
</form>
But this did not work either.
Could you please help me with this? The idea is simple but I cannot get something that works... thank you!
UPDATE
Based on the below answer from MohamedSanaulla, I decided to use the controller to do this, created a "forms" object with the required fields and edited my code as follows:
<form action="#" th:action="#{/distance/search}" th:object="${param}" method="post">`
<p>Distance: <input type="text" th:field="*{dist}"/></p>
<p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
</form>
#PostMapping("/distance/search")
public String userGetClose(#ModelAttribute ("param") Forms form) {
String distance = String.valueOf(form.getDist());
return "redirect:/distance/" + distance;
}
Ideally I wanted to create and call the url directly from the html page, to avoid going back to the controller just to create the url, but this works fine.
You have to either use ModelMap or Model in your controller and then use addAttribute to set the dist:
public String controllerMethod(#RequestParam("dist") String dist,
Model model){
model.addAttribute("dist", dist);
return "viewName";
}
Or Thymeleaf provides context object to get query params like ${param.dist} directly in your HTML
Update:
Just saw that there is a redirect: in the controller. So the dist is no longer available in the request param and hence ${param.dist} will not work. The easier solution is to use ModelMap and put the dist as part of the view model.

Confusion regarding action in the form

I am fairly new to Laravel and I am trying CRUD operations using Resource Controller. The problem I am facing is regarding what should be the action in create a task form. Let me give you an overview, how the application is designed. I have created separate directories for MVC as listed below:
Todo_Model\todo_model.php
Todo_Controller\todo_controller.php
Todo_View\home.blade.php
Todo_View\create.blade.php
Route: Route::resource('todo','Todo_Controller\todo_controller');
route:list
Controller:
public function index()
{
return view('Todo_View\home');
}
public function create()
{
return view('Todo_View\create');
}
public function store(Request $request)
{
$todo= new todo_model();
$todo->title=$request->title;
$todo->body=$request->body;
$todo->save();
return redirect('todo');
}
create.blade.php
<form method="POST" action="../todo">
{{csrf_field()}}
<tr><td><input type="text" name="title" value="" placeholder="Title"></td></tr>
<tr><td><input type="text" name="body" value="" placeholder="Body"></td></tr>
<tr><td><input type="submit" name="submit" value="Submit"></td></tr>
</form>
Now the problem is that the action of the form should be todo as can be seen in the route:list but when I hit submit with that I get MethodNotAllowedHttpException and the URL shown is http://localhost/laravel-7/blog/public/todo/todo. But during the hit and trial I figured out I should use form action as ../todo. I am highly confused as to why do I have to use that action as it doesn't make any sense because in the route list, URI is clearly mentioned as todo
Another point, when I hit index page, URI is http://localhost/laravel-7/blog/public/todo and when I get redirected from home page to create page, the URI is http://localhost/laravel-7/blog/public/todo/create
You are confusing action with native php in laravel.
Replace your action="../todo" with action="{{url('todo')}}"
<form method="POST" action="{{url('todo')}}">
{{csrf_field()}}
<tr><td><input type="text" name="title" value="" placeholder="Title"></td></tr>
<tr><td><input type="text" name="body" value="" placeholder="Body"></td></tr>
<tr><td><input type="submit" name="submit" value="Submit"></td></tr>
</form>
You just need to specify the route exactly no need for saying where the file is actually located so action="/todo" would work fine

Controller Not receiving value from span in HTML using Spring boot and Thymeleaf

I have the following content in my HTML which is using Thymeleaf
<form action="#" th:action="#{/shutDown}" th:object="${ddata}" method="post">
<span>Domain</span>
<span th:text="${domain}" th:field="*{domain}">domain</span>
<input type="Submit" value="close" />
</form>
And I have the following in my Controller which is using Sprint Boot
#RequestMapping(value = "/shutDown", method = RequestMethod.POST)
public ModelAndView shutDownPage(ModelAndView modelAndView, Authentication authentication,
#ModelAttribute("ddata") DInputBean dInputBean) {
String domain = dInputBean.getdomain();
return modelAndView;
}
I'm hoping I'd get value of domain from the HTML in the Controller but it's always null. DInputBean has getters and setters for "domain" field.
The th:field attribute can be used on <input>, <select>, or, <textarea>.
A solution you could possibly replacing you second <span> with a hidden input element.
<form action="#" th:action="#{/shutDown}" th:object="${ddata}" method="post">
<span>Domain</span>
<input type="hidden" th:field="*{domain}" th:value="${domain}" />
<input type="Submit" value="close" />
</form>
If you wanted to keep the second div, just place the <input type="hidden"> inside the second <span> and remove the th:field attribute from the second <span>.
Edit:
If you wanted to add the value of domain in a span.
<form action="#" th:action="#{/shutDown}" th:object="${ddata}" method="post">
<span>Domain</span>
<span th:text="${domain}">domain<span>
<input type="hidden" th:field="*{domain}" th:value="${domain}" />
<input type="Submit" value="close" />
</form>
http://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html#inputs
An option is to use a read-only input field:
<input type="text" th:field="*{domain}" th:value="${domain}" readonly="readonly"/>
This both displays the value and sends it on submit.
The key is to add the value of the domain variable to the form:
#GetMapping("/shutDownPage")
public String shutDownPage(Model model) {
model.addAttribute("ddata" new Ddata()); //or however you create your bean
String username = ... //however you get your username
String domain = myRepositoryService.findDomainByUsername(username);
model.addAttribute("domain", domain);
return "shutDownPage";
}
Include an HTML page in the action so that when you open the HTML page in a browser without a server/container, the button will still appear to work:
<form action="confirmationPage.html" th:action="#{/shutDown}" th:object="${ddata}" method="post">
<!-- You can benefit from using a conditional expression -->
<span th:text="${domain != null ? domain : 'No domain supplied'}">[domain]</span>
<input type="hidden" th:field="*{domain}" th:value="${domain}"/>
<input type="Submit" value="close"/>
</form>
And your post method:
#PostMapping("/shutDown") //use shorthand
public String shutDownPagePost(#ModelAttribute("ddata") DInputBean dInputBean {
String domain = dInputBean.getDomain();
//do whatever with it
return "confirmationPage";
}

Resources