How to create a dynamic link with Thymeleaf and Spring Boot? - spring

so I've been making a "website" hosted on my computer with Thymeleaf and Spring Boot, and I have a basic, insecure login that I made for fun. What I'm currently trying to do is to make a link that brings you back to a previous page based, which is sent in as a String through the model.
Here are a few snippets:
In the controller:
#RequestMapping("/")
public String index(Model model) {
return ifLoggedIn(model, "home", "");
}
private String ifLoggedIn (Model model, String location, String returnTo) {
if (Login.loggedIn.getOrDefault(Login.IP(), Boolean.FALSE)) {
return location;
} else {
model.addAttribute("login", new Login());
model.addAttribute("return_page", returnTo);
return "login";
}
}
In the loggedin.html file (After you log in successfully):
<body>
<a th:href="'/' + ${return_page}">Return to previous location</a>
</body>
EDIT:
I first change the line in the loggedin.html file to follow what Pau recommended:
<a th:href="#{'/' + ${return}}">Return to previous location</a>
But I kept getting redirected to server.local/null, so I tried removing the '/' + and having the / inside the ifLoggedIn parameters, so it would be return ifLoggedIn(model, "home", "/", but that also kept sending me to server.local/null. Now, I just changed the line in the loggedin.html file to the following:
<a th:if="(${return_page} != null)" th:with="return=(${return_page})" th:href="#{${return}}">Return to previous location</a>
Now it just disappears, meaning that ${return_page} is equal to null. Looking back at the controller I don't understand why it returns null. Another thing, I am redirecting back to other pages as well, with one line saying return ifLoggedIn(model, "messages", "/messages", but even with those I still get sent to server.local/null.

You need to use link expressions using #{...} which is a type of Thymeleaf Standard Epression. In the official doc you can see a lot of examples using expressions inside your link expressions in section 9. Using expressions in URLs:
http://www.thymeleaf.org/doc/articles/standardurlsyntax.html
No problem! Every URL parameter value is in fact an expression, so you can easily substitute your literals with any other expressions, including i18n, conditionals…:
....
the URL base itself can be specified as an expression, for example a variable expression:
<a th:href="#{${detailsURL}(id=${order.id})}">
Furthermore, in the tutorial there is a expressions which is similar as yours, in the section 4.4 Link URLs:
URL bases can also be the result of evaluating another expression:
...
<a th:href="#{'/details/'+${user.login}(orderId=${o.id})}">view</a>
So in your case, it would be like next:
<body>
<a th:href="#{'/' + ${return_page}}">Return to previous location</a>
</body>

You can Use This:
Controller
#GetMapping("/userLogin/{userId}")
public String getUserDetailsById(#PathVariable("userId") int userId, Model model){
return "user/userDetails";
}
HTML
<body>
<a th:href="#{/userLogin/{userId}(userId=${user.userId})}"></a>
</body>

<body>
<a th:href="#{'/' + return_page}">Return to previous location</a>
</body>
This works for me

For e.g. here you want to pass the dynamic value of port from your controller then do like this:-
<a th:href="#{http://localhost:{port}/index.html(post=${serverPort})}">Click here</a>

Related

thymeleaf doesn't update a model value in my template after a post

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!

Go a page instead of opening a new tab

I use thymeleaf, in a spring boot program
When an user click this link i want to goto a page
<td><a th:href="#{/editings/testament/} + ${testament.id}" target="_blank" rel="noopener noreferrer"><i class="bi bi-pencil-square"></i></a></td>
In my controller I have
#PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_USER') ")
#GetMapping(value = "/editings/testament/{testamentId}")
public String editTestament(Model model, #PathVariable Long testamentId) throws Exception {
....
model.addAttribute("testamentId", testament.getId());
model.addAttribute("testamentDocument", testament.getTestamentDocument());
...
return "starter";
}
Actually when User click the link that open another tab with the starter page...
How to avoid that
So the reason that the page is opening in a new tab is because of the target="_blank" attribute present on the a tag.
If you remove this, you should be all set. The controller shouldn't need to be edited to make this happen. Here is a resource to read more: https://www.w3schools.com/tags/att_a_target.asp
Hope you're having a good day!

ThymeLeaf: Cannot display view after calling endpoint

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).

Ajax Thymeleaf Springboot

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).

Issue with wrong controller being called in jquery ajax call

My issue is for some strange reason it seems stuck in the page controller so instead of getting out and going into the ajax controller I have it trying to go down that route in the page controller
1st try
http://localhost:2185/Alpha/Ajax/GetBlah_Name/?lname=Ge&fname=He
2nd try
http://localhost:2185/Patient/~/Ajax/GetBlah_Name/?lname=Ge&fname=He
Objective
http://localhost:2185/Ajax/GetBlah_Name/?lname=Ge&fname=He
Page button to call jquery
<a style="margin-left: 310px;" href="javascript:void(0)" onclick="getBlah()"
class="button"><span>Lookup</span></a>
Jquery code
1st try
{
$.getJSON(callbackURL + 'Ajax/GetBlah_Name/?lname=' + $('#Surname').val() + '&fname=' + $('#FirstName').val(), null, GetResults)
}
2nd try
{
$.getJSON(callbackURL + '~/Ajax/GetBlah_Name/?lname=' + $('#Surname').val() + '&fname=' + $('#FirstName').val(), null, GetResults)
}
In summary I don't know why it won't break out of the controller and go into the Ajax controller like it has done so in all the other projects I've done this in using the 1st try solution.
It seems you want to cal a controller at ~/Ajax. Is it? If yes, you should use this code:
$.getJSON(callbackURL + '/Ajax/GetBlah_Name/?lname=' + $('#Surname').val() + '&fname=' + $('#FirstName').val(), null, GetResults)
UPDATE:
This will work for your Q, but the complete solution is #Darin Dimitrov's answer. I suggest you to use that also.
UPDATE2
~ is a special character that just ASP.NET works with it! So http doesn't understand it. and if you start your url with a word -such as Ajax-, the url will be referenced from where are you now (my english is not good and I can't explain good, see example plz). For example, you are here:
http://localhost:2222/SomeController/SomeAction
when you create a link in this page, with this href:
href="Ajax/SomeAction"
that will be rendered as
http://localhost:2222/SomeController/Ajax/SomeAction
But, when url starts with /, you are referring it to root of site:
href="/Ajax/SomeAction"
will be:
http://localhost:2222/Ajax/SomeAction
Regards
There are a couple of issues with your AJAX call:
You are hardcoding routes
You are not encoding query string parameters
Here's how I would recommend you to improve your code:
// Always use url helpers when dealing with urls in an ASP.NET MVC application
var url = '#Url.Action("GetBlah_Name", "Ajax")';
// Always make sure that your values are properly encoded by using the data hash.
var data = { lname: $('#Surname').val(), fname: $('#FirstName').val() };
$.getJSON(url, data, GetResults);
Or even better. Replace your hardcoded anchor with one which will already contain the lookup url in its href property (which would of course be generated by an url helper):
<a id="lookup" href="Url.Action("GetBlah_Name", "Ajax")" class="button">
<span>Lookup</span>
</a>
and then in a separate javascript file unobtrusively AJAXify it:
$(function() {
$('#lookup').click(function() {
var data = { lname: $('#Surname').val(), fname: $('#FirstName').val() };
$.getJSON(this.href, data, GetResults);
return false;
});
});
Now how your urls will look like will totally depend on how you setup your routes in the Application_Start method. Your views and javascripts are now totally agnostic and if you decide to change your route patterns you won't need to touch jaavscript or views.

Resources