I am in the process of learning Spring Boot and became unstuck when trying to post model data to an HTML file.
I have a controller, where I populate the model and call an HTML page from. When I put a breakpoint inside this method, the model data gets populated correctly, but the HTML page is rendered with only the name of the HTML file, and nothing else (no browser errors either). I am thinking it may have something to the with the file structure, and the fact that my RestController already has a path specified (because when I create a clean new controller with no explicit class-based #RequestMapping specified and call the template from the root path + name of HTML file, it renders correctly). I do have the ThymeLeaf dependency installed, and "userView.html" is placed inside the "template" directory.
ReaderController.java extract:
#RequestMapping("/reader")
public class ReaderController {
...
#RequestMapping(value = "/userView")
public String getUser(Model model) {
// business logic goes here
model.addAttribute("userName","Somebody");
model.addAttribute("url", "www.example.com");
return "userView";
}
userView.html extract:
<body>
<h1>User Data</h1>
<p th:text="'Username: ' + ${userName}"/>
<p th:text="'Url: ' + ${url}"/>
</body>
http://localhost:8080/reader/userView only renders the word "userView".
I found the solution to the issue. I inadvertently used the #RestController annotation instead of the #Controller annotation to the controller class. This link helps to explain the issue.
Related
I have a session attribute called
#SessionAttributes("myNewAttribute")
which is defined at the top of my controller. when I hit my second endpoint, the post, the value does not update in thymleaf from blank to showing the string but it does print it to my console. I'm going a little crazy. Here is my controller file and my template:
#Controller
#SessionAttributes("myNewAttribute")
public class SpringFileController {
#GetMapping("/pageone")
public String displayPageOne(Model model) {
return "pageone";
}
#PostMapping("/clickedsavebutton")
public String handleSaveButton(Model model){
model.addAttribute("myNewAttribute","catnip");
String valueToDisplay = (String)model.getAttribute("myNewAttribute");
System.out.println(valueToDisplay);
return "pageone";
}
}
my template
<p th:text="${myNewAttribute}"></p>
<!--the save button, other things, wtc.. -->
So in my console when I click save I get "catnip" but "catnip" is not displayed on the page UNTIL I refresh the page. What am I doing wrong
edit: I added window.location.reload() after my post call in my template which makes the attribute show, but I lose other things on the page that have been changed if I do it this way
This is expected behavior, Thymeleaf is a rendering engine and as such can not change things after the initial load of the page.
To get the desired effect you'd have to reload (part) of your web page. this can be either a complete refresh or refreshing a div or field using something like ajax/javascript.
I added
$( "#required-field-div" ).load(window.location.href + " #required-field-div" );
to my function on the save button and got this to work. thank you for all the help!
I'm trying to define the action attribute of a form from the controller.
This is in my controller class:
model.addAttribute("action", "/signUnique(uClass=" + uClass + ")");
uClass is a string containing the value I want to pass in the url.
And this is in my html (thymeleaf) file:
<form th:action="#{${action}}" method="POST">
I'm getting this in the action:
/signUnique(uClass=2)
I'm new in thymeleaf and spring boot, and it is also my first time posting in stackoverflow, so please forgive me if I did something wrong.
Thx.
I would recommend you just build the entire url in your controller:
model.addAttribute("action", "/signUnique?uClass=" + uClass);
If you really do want to parse a Thymeleaf expression in your template, you'll need to use preprocessing. Something like this will work:
<form th:action="#{__${action}__}" method="POST">
I am trying to call a REST endpoint and then display a ThymeLeaf template:
The Endpoint:
#GetMapping("/devices")
public String getDeviceDetailU(Model model) {
List<FinalDevice> devices = deviceService.getAll();
model.addAttribute("devices", devices);
return "deviceList";
}
For the endpoint I tried returning /deviceList, /deviceList.html, deviceList.html.
Whenever I navigate to the endpoint, I simply get the string that was returned.
Here is the template:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<body>
Hello World!
</body>
</html>
While I understand, at this point, it will not display the list, I just want to be forwarded to the template.
If I go to localhost:8080/deviceType I display that template. This to me indicates it is not a security or configuration issue.
Any ideas?
This should all work according to this tutorial.
You probably have #RestController instead of just a #Controller.
If you want templates to be rendered you need to use #Controller. #RestController means that all your #Mappings simply serialize the return value and output it as json or xml (which is why you are seeing the string deviceList instead of the template).
I'm moving my WebForms project to MVC and having a hard time designing things.
The basic display of my app is in _Layout. The page is divided into 4 parts(say Part A,B,C and D), with 3(A,B,C) just containing html and one(D) is dynamic. I had used #RenderBody to bring in the content of Part D. However, now the other parts are changing and I need separate controllers for these parts. What is the best way to get their contents to be displayed into _Layout?
#Html.RenderPartial / #Html.Partial / #Html.RenderAction / #Html.Action ?
I'm currently trying to replace Part C by using -
#Html.Action("Index", "CController")
However, this is not working.
In Index.cshtml for CController, I've the Layout = null, initially it was set to point to _Layout.cshtml, but I read here that this created issues.
After putting the C Part in CControllers view, it does not event display the basic _Layout page that it displayed earlier.
Here's the Index.cshtml of CController -
<div id="noteContainerDiv">
Here goes all the data to display
</div>
And Here's the code for CController.cs -
public class CController : Controller
{
public ActionResult Index()
{
return PartialView();
}
}
Can anyone suggest the right way to design this?
What you could do is have a view model for your subview (the one you call D) and pass in this view model from your layout view.
I.e. by doing something like this in your _Layout:
#Html.Partial("_SubviewD", Model.SubviewDModel)
Then obviously in your controllers you need to initialize this subview model and include it in your model (or alternatively, in the ViewBag--apologies for suggested being evil!) You could even find ways to do this without changing all your controllers (for example through a base Controller class and overriding OnActionExecuted).
There are many ways you could do this. But I would stick to your initial approach.
Have your CController Index action return a PartialViewResult instead of a full result.
You don't set the layout for partial views. So in your CController index action you'll have something like this:
var model = ....
return this.PartialView("NameOfYourPartialView", yourModel);
And when you call
#Html.Action("Index", "CController")
Everything should be OK, cool?
In the application I am working on, I have an Html page inside views folder and I have mentioned the action as follows.
<form name="form" onsubmit="return validateForm();" method="post" action="//Controllers/RegistrationController.cs">
The registration controller returns a view.
public ActionResult Detail(string name)
{
return View();
}
When I run the program, I get server not found error.
I also tried changing the action string to action="//Controllers/RegistrationController.cs/Detail"
but got the same error.
Does the action string have to be written in some other way?
Thank you very much in advance for your help.
Assuming you are using the default routes ({controller}/{action}/{id}) you need:
action="/Registration/Detail"
Actually I would recommend you using HTML helpers to generate forms and never hardcode them as you did:
#using (Html.BeginForm("Details", "Registration", FormMethod.Post, new { name = "form", onsubmit = "return validateForm();" }))
{
...
}
Description
You don't have to set the path like in your solution. You don't need to set Controllers because the framework knows that you mean the controller.
Assuming that you dont change the routing in global.asax, your RegistrationController.cs has an ActionMethod called Detail (decorated with [HttpPost]) and the following folder structure within your project.
Controllers/RegistrationController.cs
Views/Registration/Detail.cshtml
#using (Html.BeginForm("Detail", "Registration", FormMethod.Post, new { #onSubmit = "return validateForm();" }))
{
// Your Form's content
}
/registration/detail - you don't need to reference the path to the actual file. The framework finds the controller class and invokes the requested action for you. It uses the routes as defined in global.asax.cs to determine the controller and action from the url. The default route is {controller}/{action}/{id} where the first two have defaults of "Home" and "Index", respectively, and the third is optional. You can change this if you want by adding/modifying the route set up.