Thymeleaf: Replace div with other pages on controllers - spring

I'm new to thymeleaf.
CustomerController.java
#RequestMapping(value = {"dashboard", ""}, method = RequestMethod.GET)
public String indexDashboard() {
return "dashboard::dashboard_index";
}
#RequestMapping(value = {"dashboard/edit", ""}, method = RequestMethod.GET)
public String editDashboard() {
return "dashboard::dashboard_edit";
}
dashboard.html
It is main page
<div id="content"></div>
dashboard_index.html
<div>dashboard_index</div>
dashboard_edit.html
<div>dashboard_edit</div>
I want replace content div with other pages on controllers.

You need to look into template fragments from the sounds of it:
http://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html#rendering-template-fragments
You can either use javascript to call a controller specific to the fragment, and then load it into your page.
OR
Load it directly using th:include or th:replace while passing the relevant values in via the same controller method.

Related

I want to assign Object field eg. greeting.method (which can be post or get ) to Form Method attribute using thymeleaf spring boot

Generally the code is -->
Image of code, click here to see the code!
I want that method which is hardcoded as "post" need to come from greeting object eg. (greeting.method)
How can I achieve that any suggestions?
So just to make it clear, you want to read the url action and method from a variable, instead of hardcoding them in thymeleaf
In that case that is actually very simple:
Pass url and method variables to the model by defining the following in your controller
#ModelAttribute("url") public String url() { return "foo/bar"; }
#ModelAttribute("method") public String method() { return "POST"; }
Define the url and method with thymeleaf: <form th:action="${url}" th:method="${method}" ...>

views in thymeleaf spring boot templates sub folder

I am using thymeleaf in spring boot, and have several views. I don't want to keep all the views in the same folder which is src/main/resources/templates by default.
Is it possible to move some of the view in src/main/resources/templates/folder1, and I will pass "folder1/viewname" to access that page?
When I tried http://localhost:8080/folder1/layout1 it didn't found my html in src/main/resources/templates/folder1/, but when I move the html in templates main folder src/main/resources/templates/, http://localhost:8080/layout1 worked fine.
My controller class looks like:
#RequestMapping(value = "{pagename}", method = RequestMethod.GET)
public String mf42_layout1(#PathVariable String pagename) {
return pagename;
}
So, I thought if I pass layout1, it will look int the templates, and if I say "a/layout1", it will look in /layout folder
Thanks,
Manish
Basically, your request mapping and the name of your view are decoupled, you just need to pay attention to the syntax.
For instance, with
#RequestMapping(value = "/foobar", method = RequestMethod.GET)
public String mf42_layout1() {
return "layout1";
}
a request to http://localhost:8080/foobar will render the template located in src/main/resources/templates/layout1.html.
It also works if you put your templates on a subfolder, as long as you provide the correct path to the view:
#RequestMapping(value = "/foobar", method = RequestMethod.GET)
public String mf42_layout1() {
return "a/layout1";
}
A request to http://localhost:8080/foobar will render the template located in src/main/resources/templates/a/layout1.html.
You can also parameterized the url endpoint with #PathVariable:
#RequestMapping(value = "/foobar/{layout}", method = RequestMethod.GET)
public String mf42_layout1(#PathVariable(value = "layout") String layout) { // I prefer binding to the variable name explicitely
return "a/" + layout;
}
Now a request to http://localhost:8080/foobar/layout1 will render the template in src/main/resources/templates/a/layout1.html and a request to http://localhost:8080/foobar/layout2 will render what's in src/main/resources/templates/a/layout2.html
But beware the forward slash acts as a separator in URLs, so with your controller:
#RequestMapping(value = "{pagename}", method = RequestMethod.GET)
public String mf42_layout1(#PathVariable String pagename) {
return pagename;
}
My guess is when you hit http://localhost:8080/a/layout1 pagename receives "a" and "layout1" is not caught. So the controller probably tries to render the contents of src/main/resources/templates/a.html
The Spring MVC reference extensively describes how to map requests, you should read it carefully.
I faced similar template not found issue when running the application in a Linux server. I was using the path as "return "/a/layout1". This worked fine in a local windows PC, but I had to remove the starting "/" to make it work in a Linux box(i.e. "return "a/layout1").

Spring modelattribute not recognized anymore after ajax update

i´ve encountered the following issue several times:
I use a Controller to bind a dto to a html form (via thymeleaf). Please note the model named "invoiceDto"
#RequestMapping(value = {"/create"}, method = RequestMethod.GET)
public String create(Locale locale, Model model) throws Exception {
final String login = this.getAuthentication().getCurrentApplicationUser();
if (login == null || login.isEmpty())
throw new Exception(this.getMessageSource().getMessage("label.findError", null, locale));
final Future<Setting> setting = settingService.findFirst();
final Future<ApplicationUserContactProjection> applicationUserContactProjection = applicationUserService.findByLogin(login);
while (!setting.isDone() && !applicationUserContactProjection.isDone()) {
Thread.sleep(100);
}
if (setting.get() == null || applicationUserContactProjection.get() == null)
throw new Exception(this.getMessageSource().getMessage("label.error.findError",
null, locale));
model.addAttribute("invoiceDto", new InvoiceDto(setting.get(), applicationUserContactProjection.get()));
model.addAttribute("message", this.getMessageSource().getMessage("label.navigation.invoiceCreation", null, locale));
return "invoice/create";
}
I have a html form (thymeleaf generated) where i use the above Java pojo dto with the given modelattribute name to fill my Input fields. This is an excerpt of it. The important part is the div with the id of "invoiceLineItems" where thymeleaf replaces its child div with a lineItems Fragment:
<form action="#" th:action="#{/invoice/newinvoice}" th:object="${invoiceDto}" role="form" method="post"
class="form-signin" id="editInvoiceForm"
accept-charset="utf-8">
<div id="invoiceLineItems"><div th:replace="invoice/items :: lineItems"></div></form>
The fragement contains the following stuff - an excerpt of it:
<td>
<input type="text"
th:field="*{items[__${index.index}__].lineItemTotalPrice}"
readonly
class="form-control" disabled
id="lineItemTotalPrice"/>
</td>
Excerpt of the given pojo:
public class InvoiceDto implements Serializable {
private Invoice invoice;
private List<LineItem> items;
I access the list like this:
th:field="*{items[__${index.index}__].lineItemTotalPrice}"
The Problem:
I can add items dynamically via Ajax. I serialize the whole form (for convenience reasons) and call a Controller Method:
#RequestMapping(value = {"/newlineitem"}, method = RequestMethod.POST)
public String newLineItem(#ModelAttribute("invoiceDto") InvoiceDto invoiceDto,
Model model)
throws Exception {
invoiceDto.addItem(new LineItem());
final Future<InvoiceDto> calculatedInvoiceDto = invoiceService.calculateInvoice(invoiceDto);
while (!calculatedInvoiceDto.isDone()) {
Thread.sleep(100);
}
model.addAttribute("invoiceDto", calculatedInvoiceDto.get());
return "invoice/dynamicitems :: lineItems";
}
As you can see, i let thymeleaf render a Special view, because after the Ajax success spring cannot set the modelattributes proper.
In short: After the Ajax Returns the partial view, the following will throw an exception:
th:field="*{items[__${index.index}__].lineItemTotalPrice}"
whereas this works - note the prefixed invoiceDto:
th:field="*{invoiceDto.items[__${index.index}__].lineItemTotalPrice}"
Question:
What´s wrong here?
Why do i have to prefix the name of the modelattribute after the partial Ajax update, whereas in the first run i don´t have to?
Thank you for your help!
EDIT:
For my share it looks like the way that spring "forgets" the originally named form modelattribute "invoiceDto" if the page is partially updated by an ajax call (to another spring controller, modifying invoiceDto) through a partial thymeleaf html.
So after the controller returns the partial thymeleaf view i have to access its fields with prefixed "invoiceDto", as if there would be no invoiceDto attribute.
Thanks again for your help!
UPDATE
As there is no progress on this i have raised a thymeleaf issue:
https://github.com/thymeleaf/thymeleaf/issues/795
Nevertheless i think this is a spring issue, because i have the same results with JSP.
Repository to comprehend this issue
https://mygit.th-deg.de/tlang/thymefail
If I understand your problem correctly, you're trying to use the *{} notation when there is no active object. When the ajax method returns just the "lineItems" fragment, Thymeleaf has no way of knowing that it belongs to a form with a th:object on it.
I guess the best solution is to return the whole form then, in js, extract the lineItems.
Or maybe just get rid of th:object altogether (convenient only when you want to show validation errors imho).

Declare ViewBag on controller

I'm using ViewBag.Message with the same message several times into the methods on the controller.
It is possible to declare ViewBag.Message on the top of the class, so can be used in the whole controller without repeat the code?
Assuming Razor syntax you can achieve this with.
#{string pageMessage = ViewBag.Message.ToString();}
then pageMessage is a local variable available to the page, for example:
<h1>#pageMessage</h1>
EDIT
ViewBag is a dynamic object which is a member of the Controller base class so to just specify this once in the whole controller you could put something in your controller constructor.
public class MyController : Controller
{
public MyController()
{
ViewBag.ViewTime = DateTime.Now.ToString();
}
// rest of controller code
}

Route values disappeare in View .Net MVC3

I have simple controller:
public class TestController : Controller
{
public ActionResult Test(string r)
{
return View();
}
}
I have simple View Test.cshtml:
<h2>#ViewContext.RouteData.Values["r"]</h2>
#using (Html.BeginForm("Test", "Test"))
{
<input type="text" name="r" />
<button>Submit</button>
}
I have route rule in Global.asax:
routes.MapRoute(
null,
"Test/{r}",
new { action = "Test", controller = "Test",
r = UrlParameter.Optional }
);
I want to make such thing: user types route value in input, press submit and controller redirects him to page Test/value. But controller show just page with name Test everytime. ViewContext.RouteData.Values["r"] is empty too. I check in debug, Test action recieves user value of r correctly.
How can I realize my idea?
Thanks.
I'm super late to the party, but just wanted to post a solution for reference. Let's assume that this form has more than just a strong as it's input. Assuming there are other inputs, we can wrap up the inputs of the form into a class in our model, called TestModel whose properties maps to the id's of the form's inputs.
In our post, we redirect to the get, passing in the route values we need in the URL. Any other data can then be shuttled to the get using a TempData.
public class TestController : Controller
{
[HttpGet]
public ActionResult Test(string r)
{
TestModel model = TempData["TestModel"] as TestModel;
return View(model);
}
[HttpPost]
public ActionResult Test(string r,TestModel model) //some strongly typed class to contain form inputs
{
TempData["TestModel"] = model; //pass any other form inputs to the other action
return RedirectToAction("Test", new{r = r}); //preserve route value
}
}
You cannot do this without javascript. There are two types of methods that exist when submitting a <form>: GET and POST. When you use POST (which is the default), the form is POSTed to the url but all data entered in input fields is part of the POST body, so it is not part of the url. When you use GET, the input fields data is part of the query string but of the form /Test?r=somevalue.
I wouldn't recommend you trying to send user input as part of the path but if you decide to go that route you could subscribe to the submit event of the form and rewrite the url:
$('form').submit(function() {
var data = $('input[name="r"]', this).val();
window.location.href = this.action + '/' + encodeURIComponent(data);
return false;
});
As far as you are saying to post the form to Html.BeginForm("Test", "Test") you will be always posted back to the same page.
A solution could be to use an explicit Redirect to the action using 'RedirectToAction' (in view) or you can use javascript to change the form's action:
<input type="text" name="r" onchange="this.parent.action = '\/Test\/'+this.value"/>

Resources