Issue
I have a clientlist.html page which is defined as such:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
<meta charset="UTF-8">
<title>Clienti</title>
<link rel="stylesheet" href="/webjars/bootstrap/4.5.0/css/bootstrap.min.css" />
</head>
<body>
<a class="button" th:href="@{/clients/form}">Adauga un client nou</a>
<label th:for="txtFilter">Nume</label>
<input th:id="txtFilter" type="text" class="form-control form-control-sm" />
<a th:href="@{/clients/getClientByLastName/{lastName}(lastName=${???})}">Cauta un client dupa nume</a>
<div>
<h2>Lista clienti</h2>
<table>
<thead>
<tr>
<th>ID</th>
<th>Prenume</th>
<th>Nume</th>
</tr>
</thead>
<tbody>
<tr th:each="client : ${clients}">
<td th:text="${client.getClientID()}"></td>
<td th:text="${client.getLastName()}"></td>
<td th:text="${client.getFirstName()}"></td>
<td><a th:href="@{/clients/form/{id}(id=${client.getClientID()})}">Editeaza</a></td>
<td><a th:href="@{/clients/delete/{id}(id=${client.getClientID()})}">Sterge</a></td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
I have an tag that has a th:href which refers to the method in my ClientController, which is defined like so:
import com.gestiunezboruri.demo.model.Client;
import com.gestiunezboruri.demo.service.ClientService;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
@Slf4j
@Controller
public class ClientController {
@Autowired
ClientService clientService;
Logger logger = LoggerFactory.getLogger(ClientController.class);
@ModelAttribute("clients")
public List<Client> populateClientList() {
return clientService.findAll();
}
@RequestMapping("/clients/getAll")
public String renderClientList() {
return "clientlist";
}
@RequestMapping(value = "/clients/form")
public String renderClientForm(Model model) {
model.addAttribute("client", new Client());
return "clientform";
}
@GetMapping("/clients/form/{id}")
public String getClientById(@PathVariable Long id, Model model) {
model.addAttribute("client", clientService.findById(id));
return "clientform";
}
@GetMapping("/clients/getClientByLastName/{lastName}")
public String getClientByLastName(@PathVariable String lastName, Model model) {
model.addAttribute("clients", clientService.findByLastNameLike(lastName));
for(Client c : clientService.findByLastNameLike(lastName)) {
log.info(c.toString());
}
return "clientform";
}
//TODO: @GetMapping("clients/resetFilters")
@PostMapping("/clients/save")
public String saveClient(@Valid Client client, BindingResult bindingResult) {
if (bindingResult.hasErrors()){
return "clientform";
}
clientService.save(client);
log.info("A fost adaugat clientul cu urmatoarele date: ID = " + client.getClientID() + "; Nume = " + client.getLastName() + "; Prenume = " + client.getFirstName());
return "redirect:/clients/getAll";
}
@RequestMapping("/clients/delete/{id}")
public String deleteClient(@PathVariable Long id) {
Client clientSters = clientService.findById(id);
clientService.deleteById(id);
log.info("A fost sters clientul cu urmatoarele date: ID = " + clientSters.getClientID() + "; Nume = " + clientSters.getLastName() + "; Prenume = " + clientSters.getFirstName());
return "redirect:/clients/getAll";
}
}
Defined within the table tag lies a list of all the clients in the database.
Then, there is
<input th:id="txtFilter" type="text" class="form-control form-control-sm" />
This input is supposed to be used by the user to filter the list by a value he inputs.
My question is, how can I pass the value of the txtFilter
input to the <a th:href="@{/clients/getClientByLastName/{lastName}(lastName=${???})}">
as a PathVariable? (i.e in the (lastName=${???})
part)
What I tried so far is getting the value of the input via JavaScript, like so:
var btnFilterList = document.getElementById("btnFilterList");
var txtFilter = document.getElementById("txtFilter");
console.log(txtFilter.getAttribute("value"));
btnFilterList.setAttribute("href", "@{/clients/getClientByLastName/{lastName}(lastName=${" + txtFilter.value + "})}")
and also:
var btnFilterList = document.getElementById("btnFilterList");
var txtFilter = document.getElementById("txtFilter");
console.log(txtFilter.getAttribute("value"));
btnFilterList.setAttribute("th:href", "@{/clients/getClientByLastName/{lastName}(lastName=${" + txtFilter.value + "})}")
But none seemed to work.
Solution
What you are trying to do is not making much sense. Thymeleaf is a server-side rendering templating engine. It takes your template, uses the model to populate the variables and output static HTML that is sent to the browser. Once it is at the browser, Thymeleaf is no longer "active". So all you can do if you want to change things is use JavaScript, or do a new HTTP request to a Controller method.
So if you don't want to go into JavaScript, then replace the tag to a button that sits inside a form:
<form th:action="@{/clients/searchByLastName}" method="post" th:object="${searchFormData}">
<input type="text" th:value="${searchText}">
<button type="submit">Search</button>
</form>
In your controller, you do something like this:
@PostMapping("/clients/searchByLastName")
public String searchByLastName(ModelAttribute("searchFormData") SearchFormData formData, Model model) {
String text = formData.getSearchText();
// Use text to search
...
// Fill in the found clients into the Model
...
//return the template you want to use here
return "...";
}
Use this object to transfer the searchText between the form and the controller:
public class SearchFormData {
private String searchText;
// getter and setter here
}
If you do want to use JavaScript, then you should not use anything Thymeleaf related there, that won't work.
Answered By - Wim Deblauwe