I would like to know, how I can get the User object from Thymeleaf. Currently I am calling my userService, which will get the user from the DB. I don't like this approach, because for every call a db query will be made.
Is it possible to get the user from memory?
<link href="/css/style2.css"
th:if="${#commanderService.getCurrentCommander()} and
${#commanderService.getCurrentCommander().settings == 'template=1'}"
rel="stylesheet" type="text/css" />
CommanderService:
public Commander getCurrentCommander() {
Object principal =
SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Commander commander = findByName((String)principal);
return commander;
}
If you are using spring security and thymeleaf, you could check:
https://github.com/thymeleaf/thymeleaf-extras-springsecurity3
For example:
<div sec:authentication="name">
The value of the "name" property of the authentication object should appear here.
</div>
<div sec:authorize="hasRole('ROLE_ADMIN')">
This content is only shown to administrators.
</div>
Related
I am writing a spring boot application and have encountered the problem of sharing resources for different users. Simplified by example, it looks like this: there is one variable. You can assign a value to it through the form on the page. If the first user assigns the value hello java from one browser, then the second user will see the same value through another browser. I dont know how to make each user work with their own variable and their values do not overlap?
Controller:
#Controller
public class MessageController {
private String message;
#GetMapping(value = "/show_message")
public String showMessage(Model model){
model.addAttribute("message", message);
return "message";
}
#PostMapping(value = "set_message")
public String setMessage(#RequestParam(name = "newMessage") String newMessage){
message = newMessage;
return "redirect:/show_message";
}
}
html page:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
</head>
<body>
<p>Message value: <th:block th:utext="${message}"/></p>
<p>Enter a new message value</p>
<form method="POST" th:action="#{/set_message}">
<input type="text" name="newMessage"/>
<input type="submit" value="Send"/>
</form>
</body>
</html>
The rule of thumb is that controllers as well as services are not designed to maintain a state, particularly the client state.
Besides, Spring beans are by default singletons : they are shared among clients/requests. That explains the behavior that you notice.
To solve that issue you may use the in-memory HttpSession (old practice) but that is not advised any longer because it sticks the client/user to a specific server instance that has first served his request.
You should preferably either resend the data at each request (which may be cumbersome to do) or better store the data in an in-memory database such as Redis.
That is fast and it doesn't stick the client to a specific instance of your spring boot application.
With Spring, a good association with the in-memory database usage to handle the user state is using HttpSession but Spring Session backed to a database (Redis or another).
For how to use the Session with Spring MVC, you have that good 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 downloading a PDF file with a web app, developed with Spring Boot 2.0.3 and using Thymeleaf, from an admin section, protected with Spring Security. Locally it works fine, but online I get this error:
org.thymeleaf.exceptions.TemplateInputException: Error resolving template "/email/confirmedbooking", template might not exist or might not be accessible by any of the configured Template Resolvers] with root cause
This is the controller:
#GetMapping("/admin/bookings/booking-pdf")
public void generatePdfBooking(#RequestParam Long idbooking, HttpServletResponse response)
throws IOException, Exception{
bookingService.setBookingService(idbooking);
Booking booking = bookingService.getBooking();
Guest guest = bookingService.getGuest();
String idlanguage;
if(guest.getIdlanguage() != null){
idlanguage = guest.getIdlanguage();
} else {idlanguage = "en";}
Map<String, Object> map = new HashMap<>();
map.put("booking", booking);
map.put("guest", guest);
byte[] data = pdfGenerator.createPdf("/email/confirmedbooking", map, idlanguage);
pdfGenerator.streamReport(response, data, "id-" + booking.getIdbooking() + ".pdf");
}
This is an extract of the html page:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<link rel="stylesheet" href="/webjars/bootstrap /css/bootstrap.min.css"/>
<link rel="stylesheet" href="/css/rentalwebs.css"/>
</head>
<body>
<table style="width:680px" class="table table-borderless">
<tbody>
<tr>
<td>
<h2 th:text="${property.name}"></h2>
</td>
</tr>
<tr>
<td>
<span th:text="#{booking.id}"></span>
<span th:text="${booking.idbooking}"></span>
....
As a template generator I'm using org.xhtmlrenderer.pdf.ITextRenderer (flying-saucer-pdf).
I've tested providing different .html files to generate the PDF at pdfGenerator.createPdf("/email/confirmedbooking", map, idlanguage);, but the result is always the same.
Following the advice from Daniel Mikusa, I have been able to solve the issue, taking out the first / from /email/confirmedbooking, leaving the code like this:
byte[] data = pdfGenerator.createPdf("email/confirmedbooking", map, idlanguage);
It seems that it didn't recognise the path, probably because of the double slash issue, already explained at this post: Error resolving template with Spring Boot using Thymeleaf packaged in a jar
I'm trying to use ajax with thymeleaf. I designed a simple html page with two input field. I would like to use addEventHandler for the value of first input text, then I want to send it to controller and make calculation, after that I need to write it in same html form in the second field which returns from controller.
For example:
first input text value -> controller (make calculation) -> (write value) in second input text.
My html page is
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<link rel='stylesheet prefetch' href='http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css'>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<input type="text" name="Thing" value=""/>
<script th:inline="javascript">
window.onload = function () {
/* event listener */
document.getElementsByName("Thing")[0].addEventListener('change', doThing);
/* function */
function doThing() {
var url = '#{/testurl}';
$("#fill").load(url);
alert('Horray! Someone wrote "' + this.value + '"!');
}
}
</script>
<!-- Results block -->
<div id="fill">
<p th:text="${responseMsg}"/></div>
</div>
</body>
</html>
My controller
#RequestMapping(value = "/testurl", method = RequestMethod.GET)
public String test(Model model) {
model.addAttribute("responseMsg","calcualted value")
return "test";
}
However I cannot call controller from ajax. Could you help me?
There are a few issues with your code. First of all, it looks like you're using the same template for both the initial loading of the application, and returning the calculated result.
You should split these two into different calls if you're using AJAX, since one of the goals of AJAX is that you don't need to reload an entire page for one change.
If you need to return a simple value, you should use a separate request method like this:
#GetMapping("/calculation")
#ResponseBody
public int multiply(#RequestParam int input) {
return input * 2; // The calculation
}
What's important to notice here is that I'm using #ResponseBody and that I'm sending the input to this method as a #RequestParam.
Since you will be returning the calculated value directly, you don't need the Model, nor the responseMsg. So you can remove that from your original request mapping.
You can also remove it from your <div id="fill">, since the goal of your code is to use AJAX to fill this element and not to use Thymeleaf. So you can just have an empty element:
<div id="fill">
</div>
Now, there are also a few issues with your Thymeleaf page. As far as I know, '#{/testurl}' is not the valid syntax for providing URLs. The proper syntax would be to use square brackets:
var url = [[#{/calculation}]];
You also have to make sure you change the url to point to the new request mapping. Additionally, this doesn't look as beautiful since it isn't valid JavaScript, the alternative way to write this is:
var url = /*[[ #{/calculation} ]]*/ null;
Now, your script has also a few issues. Since you're using $().load() you must make sure that you have jQuery loaded somewhere (this looks like jQuery syntax so I'm assuming you want to use jQuery).
You also have to send your input parameter somehow. To do that, you can use the event object that will be passed to the doThing() function, for example:
function doThing(evt) {
var url = [[#{/calculation}]];
$("#fill").load(url + '?input=' + evt.target.value);
alert('Horray! Someone wrote "' + this.value + '"!');
}
As you can see, I'm also adding the ?input=, which will allow you to send the passed value to the AJAX call.
Finally, using $().load() isn't the best way to work with AJAX calls unless you try to load partial HTML templates asynchronously. If you just want to load a value, you could use the following code in stead:
$.get({
url: /*[[ #{/calculation} ]]*/ null,
data: { input: evt.target.value }
}).then(function(result) {
$('#fill').text(result);
});
Be aware that $.get() can be cached by browsers (the same applies to $().load() though). So if the same input parameter can lead to different results, you want to use different HTTP methods (POST for example).
I am evaluating spring boot + MVC + bootstrap . One problem I am facing is bootstrap's navbar highlighting problem in thymeleaf.
I hope thymeleaf can judge the tab which should be highlighted.
I searched and found this solution : Bootstrap Navbar Highlighting in Thymeleaf
In the containing(outer) page , it uses
<div th:replace="header::header('home')">
to be replaced header
</div>
to designate the home tab should be highlighted.
And in the contained (inner) page , it uses
<nav class="..." th:fragment="header(activeTab)">
<ul class="nav navbar-nav">
<li th:class="${activeTab == 'home'} ? 'active' : null ">Home</li>
</ul>
to judge this tab should be highlighted or not.
It works for every single page well , but not for layout.
In a layout page , the containing (outer) page is a decorator , which decorates other pages (home / about / contact ...) . The tab value is pending here .
for example
<div th:replace="header::header('home')">
to be replaced header
</div>
<div layout:fragment="content">
layout
</div>
<div th:replace="footer::footer">
to be replaced footer
</div>
I cannot pre-assign home tab in the layout file.
Is there any way to solve it ?
Can it judge by controller or even controller's method ?
environment :
springboot.version 1.3.0.M5
spring.version :4.2.1.RELEASE
Thanks a lot !
It is not a exact solution for your question but maybe you will like the idea. You may use request context path to recognize which tab is actually selected, so you can use ${#httpServletRequest.getContextPath()} and maybe then something like that:
<ul class="nav navbar-nav" th:with="view=${#httpServletRequest.getServletPath()}">
<li th:classappend="${#strings.startsWith(view,'/home')? 'active' : ''}">Home</li>
</ul>
Or you can use ControllerAdvice:
#ControllerAdvice(assignableTypes = { MyController.class })
public class MyControllerAdvice {
#ModelAttribute
public void addAttributes(#RequestParam Map<String, String> params, Model model, HttpServletRequest request) {
String activeTab = ... whatever
model.addAttribute("active_tab", activeTab);
}
}
There is one big disadvantage of using #ControllerAdvice. If you are planning to use Spring WebFlow to create multi-page forms (wizards) then it will not work because WebFlow does not use model attributes.
change this line <nav class="..." th:fragment="header(activeTab)">
to this
<nav class="..." th:fragment="header(activeTab='activeTab')">
it worked for me