I want to fill the form according to the related book when I click the view or update the link on the page. I know, there is a solution with opening another page but I want to do it on the same page. As you can see on the picture below I can properly get the list on the left table. I have tried a post method below but did not work. So what would you recommend to do it?
Controller class:
#PostMapping(path = "/listbooks")
public String getBook(#ModelAttribute BookConfig bookConfig, Model model)
throws IOException {
model.addAttribute("book", bookConfig);
return "list";
}
#GetMapping(path = "/listbooks")
public String showAllBooks(Model model) throws IOException {
model.addAttribute("books", bookService.getBookConfigList());
return "list";
}
HTML file:
<div class="table-responsive" th:if="${not #lists.isEmpty(books)}">
<table class="table table-hover" style="height:50px;">
<thead class="thead-inverse">
<tr>
<th>Name</th>
<th>View</th>
<th>Update</th>
<th>Delete</th>
</tr>
</thead>
<tr th:each="book : ${books}">
<td th:text="${book.name}">Book Name</td>
<td>View</td>
<td>Update</td>
<td>Delete</td>
</tr>
</table>
</div>
This is what I am trying to do on the HTML file:
<form th:if="${book != null}" th:object="${book}" th:action="#{/book/}"
method="post">
<div class="panel-heading">
<h4 class="panel-title"
">Edit
Book Configuration</h4>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-3 form-group"
>
<label>Book name</label>
<input type="text" class="form-control" th:field="*{name}"/>
</div>
...
I have solved using JavaScript, Firstly, I have adjusted the getBook method below
#PostMapping("/books")
public String getBook(#RequestBody String bookName) throws IOException {
return "list";
}
and then I have add these two JS functions:
$(document).ready(function () {
$(".view").click(function () {
var $row = $(this).closest("tr"); // Find the row
var $text = $row.find(".bookname").text(); // get the text on the view link using its the class name
$.post("http://localhost:8081/books",
{
bookName: $text
},
function (data, status) {
assignDataToTable(data);
});
});
});
function assignDataToTable(data) {
alert("hey" + data);
document.getElementById("booknameinput").value = data;
}
I am new both to Spring Boot and Thymeleaf, and want to pass the value of a variable defined in the Java code to the HTML page. I searched the Web, but probably, I have overseen something important. I try to do it with the following code:
Favorite.java:
#Getter
#Setter
public class Favorite {
private String id;
private String target;
public Favorite(final String id, final String target) {
setId(id);
setTarget(this.target);
}
}
PortalController.java:
public class PortalController {
private final List<Favorite> myFavorites = new ArrayList<>();
#ModelAttribute("myFavorites")
public List<Favorite> myFavorites() {
if (myFavorites.size() == 0) {
myFavorites.add(new Favorite("ZEMPLOYEE_WORKTIME_ZWD_ESS_ABW", "ABC"));
myFavorites.add(new Favorite("ZEMPLOYEE_WORKTIME_CATS", "DEF"));
myFavorites.add(new Favorite("ZEMPLOYEE_WORKTIME_PEP_WISH_PLAN", "XYZ"));
}
return myFavorites;
}
index.html:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" lang="de" xml:lang="de">
<head>
[…]
</head>
<body>
[…]
<ul>
<div th:switch="${not #lists.isEmpty(myFavorites)}">
<div th:case="true">
<div th:each="myFavorite : ${myFavorites}">
<li>
<td th:text="${myFavorite.id}"></td>
<td th:text="${myFavorite.task}"></td>
</li>
</div>
</div>
<div th:case="*">
Nothing to show!
</div>
</div>
</ul>
[…]
I get the "Nothing to show!" text, which means that myFavorites is empty. What do I miss or what did I misunderstood about this?
Edit:
I modified the PortalController after reading The #ModelAttribute in Depth to this:
public class PortalController {
private final List<Favorite> myFavorites = new ArrayList<>();
private final Map<String, List<Favorite>> favoritesMap = new HashMap<>();
#RequestMapping(value = "/getMyFavorites", method = RequestMethod.POST)
public String submit(#ModelAttribute("myFavorites") final List<Favorite> favorites,
final BindingResult result, final ModelMap model) {
if (result.hasErrors()) return "error";
model.addAttribute("myFavorites", favorites);
favoritesMap.put(favorites.toString(), favorites);
return "favoritesView";
}
#ModelAttribute
public void getMyFavorites(final Model model) {
if (myFavorites.size() == 0) {
myFavorites.add(new Favorite("ZEMPLOYEE_WORKTIME_ZWD_ESS_ABW", "ABC"));
[…]
}
model.addAttribute("myFavorites", myFavorites);
}
Unfortunately, there is still something missing or misunderstood by me so that the Web page returns still "Nothing to show!".
Edit 2:
This is the current state I have after reading the documentation requested here:
PortalController.java:
public class PortalController {
private final List<Favorite> myFavorites = new ArrayList<>();
#RequestMapping(value = "/getMyFavorites", method = RequestMethod.GET)
public String submit(#ModelAttribute("myFavorite") final List<Favorite> favorites,
final BindingResult result, final ModelMap model) {
if (result.hasErrors()) return "error";
model.addAttribute("myFavorites", favorites);
return "favoritesView";
}
#ModelAttribute
public void getMyFavorites(final Model model) {
if (myFavorites.size() == 0) {
myFavorites.add(new Favorite("ZEMPLOYEE_WORKTIME_ZWD_ESS_ABW", "ABC"));
[…]
}
model.addAttribute("myFavorites", myFavorites);
}
index.html:
<ul>
<div th:switch="${not #lists.isEmpty(myFavorite)}">
<div th:case="true">
<div th:each="myFavorite : ${myFavorites}">
<li>
<td th:text="${myFavorite.id}"></td>
<td th:text="${myFavorite.task}"></td>
</li>
</div>
</div>
<div th:case="false">
Nothing to show!
</div>
</ul>
But I still get "Nothing to show!", as ${myFavorites} is empty.
According to the documentation #ModelAttribute should be used with #RequestMapping. You can find a more detailed description of how this annotation works here.
Also for showing a table you can change your thymeleaf code to:
<table>
<thead>
<tr>
<th> id</th>
<th> target</th>
</tr>
</thead>
<tbody>
<tr th:if="${myFavorites.empty}">
<td colspan="2"> No Info </td>
</tr>
<tr th:each="myFavorite: ${myFavorites}">
<td><span th:text="${myFavorite.id}"> ID</span></td>
<td><span th:text="${myFavorite.target}"> Target</span></td>
</tr>
</tbody>
</table>
I'm in the process of upgrading our web app to use Wicket 7 (was using 6.19).
The first page is a login screen, but for some reason, the form's onSubmit() method isn't being called, so on clicking the submit button, I just get the login page re-displayed.
I've consulted the Wicket 7 migration guide, which doesn't mention any specific changes in this area.
It's a pretty straightforward case, as you can see, it's a simple form containing username and password fields
<form wicket:id="loginform" id="loginform" >
<table style="display: table; border: 0px; margin: auto;">
<tr style="display: table-row;">
<td class="login" colspan="2"><span wicket:id="feedback">Feedback</span></td>
</tr>
<tr style="display: table-row;">
<td class="login">
<label for="username"><wicket:message key="username">Username</wicket:message>: </label>
</td>
<td class="login">
<input wicket:id="username" id="username" type="text" name="user" value="" size="30" maxlength="50"/>
</td>
</tr>
<tr style="display: table-row;">
<td class="login">
<label for="password"><wicket:message key="password">Password</wicket:message>: </label>
</td>
<td class="login">
<input wicket:id="password" id="password" type="password" name="pswd" value="" size="30" maxlength="16"/>
</td>
</tr>
<tr style="display: table-row;">
<td class="login"> </td>
<td class="login"><input class="btn" type="submit" name="Login" value="Login" wicket:message="title:loginButtonTitle"/></td>
</tr>
</table>
</form>
Here's the Java code setting up the page components -
public class Login extends UnSecurePageTemplate {
private static final long serialVersionUID = -7202246935258483555L;
#SpringBean private IBrandingService brandingService;
#SpringBean private IRemonService remonService;
#SpringBean private IUserAdminService userAdminService ;
private static final Logger logger = LoggerFactory.getLogger( Login.class);
public Login() {
this(new PageParameters());
}
public Login(PageParameters pageParameters) {
super(pageParameters);
BrandingThemeProperties properties = brandingService.getBrandingThemeProperties();
String welcomeLabel = properties.getProperty("welcome-label");
add(new Label("welcome", welcomeLabel));
add(new Label("loginHeader", getStringFromPropertiesFile("loginInstruction", this)));
LoginForm form = new LoginForm("loginform", new SimpleUser(), pageParameters);
form.add(new FeedbackPanel("feedback"));
add(form);
}
And here's the Login form (the login() method authenticates the user and returns another page) -
public final class LoginForm extends Form<SimpleUser>
{
PageParameters pageParameters;
public LoginForm(String id, SimpleUser simpleUser, PageParameters pageParameters)
{
super(id, new CompoundPropertyModel<SimpleUser>(simpleUser));
this.pageParameters = pageParameters;
add(new TextField<String>("username").setRequired(true).add(StringValidator.maximumLength(50)));
add(new PasswordTextField("password").setResetPassword(true).add(StringValidator.maximumLength(50)));
}
/**
* Called upon form submit. Attempts to authenticate the user.
*/
protected void onSubmit()
{
SimpleUser user = getModel().getObject();
String username = user.getUsername();
String password = user.getPassword();
login(username, password, pageParameters);
}
}
I also tried using a submit Button, but its onSubmit() wasn't called either.
I am having a lot of difficulty with POSTing back a form to the controller, which should contain simply an arraylist of objects that the user may edit.
The form loads up correctly, but when it's posted, it never seems to actually post anything.
Here is my form:
<form action="#" th:action="#{/query/submitQuery}" th:object="${clientList}" method="post">
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>Select</th>
<th>Client ID</th>
<th>IP Addresss</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr th:each="currentClient, stat : ${clientList}">
<td><input type="checkbox" th:checked="${currentClient.selected}" /></td>
<td th:text="${currentClient.getClientID()}" ></td>
<td th:text="${currentClient.getIpAddress()}"></td>
<td th:text="${currentClient.getDescription()}" ></td>
</tr>
</tbody>
</table>
<button type="submit" value="submit" class="btn btn-success">Submit</button>
</form>
Above works fine, it loads up the list correctly. However, when I POST, it returns a empty object (of size 0). I believe this is due to the lack of th:field, but anyway here is controller POST method:
...
private List<ClientWithSelection> allClientsWithSelection = new ArrayList<ClientWithSelection>();
//GET method
...
model.addAttribute("clientList", allClientsWithSelection)
....
//POST method
#RequestMapping(value="/submitQuery", method = RequestMethod.POST)
public String processQuery(#ModelAttribute(value="clientList") ArrayList clientList, Model model){
//clientList== 0 in size
...
}
I have tried adding a th:field but regardless of what I do, it causes an exception.
I've tried:
...
<tr th:each="currentClient, stat : ${clientList}">
<td><input type="checkbox" th:checked="${currentClient.selected}" th:field="*{}" /></td>
<td th th:field="*{currentClient.selected}" ></td>
...
I cannot access currentClient (compile error), I can't even select clientList, it gives me options like get(), add(), clearAll() etc, so it things it should have an array, however, I cannot pass in an array.
I've also tried using something like th:field=${}, this causes runtime exception
I've tried
th:field = "*{clientList[__currentClient.clientID__]}"
but also compile error.
Any ideas?
UPDATE 1:
Tobias suggested that I need to wrap my list in a wraapper. So that's what I did:
ClientWithSelectionWrapper:
public class ClientWithSelectionListWrapper {
private ArrayList<ClientWithSelection> clientList;
public List<ClientWithSelection> getClientList(){
return clientList;
}
public void setClientList(ArrayList<ClientWithSelection> clients){
this.clientList = clients;
}
}
My page:
<form action="#" th:action="#{/query/submitQuery}" th:object="${wrapper}" method="post">
....
<tr th:each="currentClient, stat : ${wrapper.clientList}">
<td th:text="${stat}"></td>
<td>
<input type="checkbox"
th:name="|clientList[${stat.index}]|"
th:value="${currentClient.getClientID()}"
th:checked="${currentClient.selected}" />
</td>
<td th:text="${currentClient.getClientID()}" ></td>
<td th:text="${currentClient.getIpAddress()}"></td>
<td th:text="${currentClient.getDescription()}" ></td>
</tr>
Above loads fine:
Then my controller:
#RequestMapping(value="/submitQuery", method = RequestMethod.POST)
public String processQuery(#ModelAttribute ClientWithSelectionListWrapper wrapper, Model model){
...
}
The page loads correctly, the data is displayed as expected. If I post the form without any selection I get this:
org.springframework.expression.spel.SpelEvaluationException: EL1007E:(pos 0): Property or field 'clientList' cannot be found on null
Not sure why it's complaining
(In the GET Method it has: model.addAttribute("wrapper", wrapper);)
If I then make a selection, i.e. tick the first entry:
There was an unexpected error (type=Bad Request, status=400).
Validation failed for object='clientWithSelectionListWrapper'. Error count: 1
I'm guessing my POST controller is not getting the clientWithSelectionListWrapper. Not sure why, since I have set the wrapper object to be posted back via the th:object="wrapper" in the FORM header.
UPDATE 2:
I've made some progress! Finally the submitted form is being picked up by the POST method in controller. However, all the properties appear to be null, except for whether the item has been ticked or not. I've made various changes, this is how it is looking:
<form action="#" th:action="#{/query/submitQuery}" th:object="${wrapper}" method="post">
....
<tr th:each="currentClient, stat : ${clientList}">
<td th:text="${stat}"></td>
<td>
<input type="checkbox"
th:name="|clientList[${stat.index}]|"
th:value="${currentClient.getClientID()}"
th:checked="${currentClient.selected}"
th:field="*{clientList[__${stat.index}__].selected}">
</td>
<td th:text="${currentClient.getClientID()}"
th:field="*{clientList[__${stat.index}__].clientID}"
th:value="${currentClient.getClientID()}"
></td>
<td th:text="${currentClient.getIpAddress()}"
th:field="*{clientList[__${stat.index}__].ipAddress}"
th:value="${currentClient.getIpAddress()}"
></td>
<td th:text="${currentClient.getDescription()}"
th:field="*{clientList[__${stat.index}__].description}"
th:value="${currentClient.getDescription()}"
></td>
</tr>
I also added a default param-less constructor to my wrapper class and added a bindingResult param to POST method (not sure if needed).
public String processQuery(#ModelAttribute ClientWithSelectionListWrapper wrapper, BindingResult bindingResult, Model model)
So when an object is being posted, this is how it is looking:
Of course, the systemInfo is supposed to be null (at this stage), but the clientID is always 0, and ipAddress/Description always null. The selected boolean is correct though for all properties. I'm sure I've made a mistake on one of the properties somewhere. Back to investigation.
UPDATE 3:
Ok I've managed to fill up all the values correctly! But I had to change my td to include an <input /> which is not what I wanted... Nonetheless, the values are populating correctly, suggesting spring looks for an input tag perhaps for data mapping?
Here is an example of how I changed the clientID table data:
<td>
<input type="text" readonly="readonly"
th:name="|clientList[${stat.index}]|"
th:value="${currentClient.getClientID()}"
th:field="*{clientList[__${stat.index}__].clientID}"
/>
</td>
Now I need to figure out how to display it as plain data, ideally without any presence of an input box...
You need a wrapper object to hold the submited data, like this one:
public class ClientForm {
private ArrayList<String> clientList;
public ArrayList<String> getClientList() {
return clientList;
}
public void setClientList(ArrayList<String> clientList) {
this.clientList = clientList;
}
}
and use it as the #ModelAttribute in your processQuery method:
#RequestMapping(value="/submitQuery", method = RequestMethod.POST)
public String processQuery(#ModelAttribute ClientForm form, Model model){
System.out.println(form.getClientList());
}
Moreover, the input element needs a name and a value. If you directly build the html, then take into account that the name must be clientList[i], where i is the position of the item in the list:
<tr th:each="currentClient, stat : ${clientList}">
<td><input type="checkbox"
th:name="|clientList[${stat.index}]|"
th:value="${currentClient.getClientID()}"
th:checked="${currentClient.selected}" />
</td>
<td th:text="${currentClient.getClientID()}" ></td>
<td th:text="${currentClient.getIpAddress()}"></td>
<td th:text="${currentClient.getDescription()}" ></td>
</tr>
Note that clientList can contain null at
intermediate positions. Per example, if posted data is:
clientList[1] = 'B'
clientList[3] = 'D'
the resulting ArrayList will be: [null, B, null, D]
UPDATE 1:
In my exmple above, ClientForm is a wrapper for List<String>. But in your case ClientWithSelectionListWrapper contains ArrayList<ClientWithSelection>. Therefor clientList[1] should be clientList[1].clientID and so on with the other properties you want to sent back:
<tr th:each="currentClient, stat : ${wrapper.clientList}">
<td><input type="checkbox" th:name="|clientList[${stat.index}].clientID|"
th:value="${currentClient.getClientID()}" th:checked="${currentClient.selected}" /></td>
<td th:text="${currentClient.getClientID()}"></td>
<td th:text="${currentClient.getIpAddress()}"></td>
<td th:text="${currentClient.getDescription()}"></td>
</tr>
I've built a little demo, so you can test it:
Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
ClientWithSelection.java
public class ClientWithSelection {
private Boolean selected;
private String clientID;
private String ipAddress;
private String description;
public ClientWithSelection() {
}
public ClientWithSelection(Boolean selected, String clientID, String ipAddress, String description) {
super();
this.selected = selected;
this.clientID = clientID;
this.ipAddress = ipAddress;
this.description = description;
}
/* Getters and setters ... */
}
ClientWithSelectionListWrapper.java
public class ClientWithSelectionListWrapper {
private ArrayList<ClientWithSelection> clientList;
public ArrayList<ClientWithSelection> getClientList() {
return clientList;
}
public void setClientList(ArrayList<ClientWithSelection> clients) {
this.clientList = clients;
}
}
TestController.java
#Controller
class TestController {
private ArrayList<ClientWithSelection> allClientsWithSelection = new ArrayList<ClientWithSelection>();
public TestController() {
/* Dummy data */
allClientsWithSelection.add(new ClientWithSelection(false, "1", "192.168.0.10", "Client A"));
allClientsWithSelection.add(new ClientWithSelection(false, "2", "192.168.0.11", "Client B"));
allClientsWithSelection.add(new ClientWithSelection(false, "3", "192.168.0.12", "Client C"));
allClientsWithSelection.add(new ClientWithSelection(false, "4", "192.168.0.13", "Client D"));
}
#RequestMapping("/")
String index(Model model) {
ClientWithSelectionListWrapper wrapper = new ClientWithSelectionListWrapper();
wrapper.setClientList(allClientsWithSelection);
model.addAttribute("wrapper", wrapper);
return "test";
}
#RequestMapping(value = "/query/submitQuery", method = RequestMethod.POST)
public String processQuery(#ModelAttribute ClientWithSelectionListWrapper wrapper, Model model) {
System.out.println(wrapper.getClientList() != null ? wrapper.getClientList().size() : "null list");
System.out.println("--");
model.addAttribute("wrapper", wrapper);
return "test";
}
}
test.html
<!DOCTYPE html>
<html>
<head></head>
<body>
<form action="#" th:action="#{/query/submitQuery}" th:object="${wrapper}" method="post">
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>Select</th>
<th>Client ID</th>
<th>IP Addresss</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr th:each="currentClient, stat : ${wrapper.clientList}">
<td><input type="checkbox" th:name="|clientList[${stat.index}].clientID|"
th:value="${currentClient.getClientID()}" th:checked="${currentClient.selected}" /></td>
<td th:text="${currentClient.getClientID()}"></td>
<td th:text="${currentClient.getIpAddress()}"></td>
<td th:text="${currentClient.getDescription()}"></td>
</tr>
</tbody>
</table>
<button type="submit" value="submit" class="btn btn-success">Submit</button>
</form>
</body>
</html>
UPDATE 1.B:
Below is the same example using th:field and sending back all other attributes as hidden values.
<tbody>
<tr th:each="currentClient, stat : *{clientList}">
<td>
<input type="checkbox" th:field="*{clientList[__${stat.index}__].selected}" />
<input type="hidden" th:field="*{clientList[__${stat.index}__].clientID}" />
<input type="hidden" th:field="*{clientList[__${stat.index}__].ipAddress}" />
<input type="hidden" th:field="*{clientList[__${stat.index}__].description}" />
</td>
<td th:text="${currentClient.getClientID()}"></td>
<td th:text="${currentClient.getIpAddress()}"></td>
<td th:text="${currentClient.getDescription()}"></td>
</tr>
</tbody>
When you want to select objects in thymeleaf, you dont actually need to create a wrapper for the purpose of storing a boolean select field. Using dynamic fields as per the thymeleaf guide with syntax th:field="*{rows[__${rowStat.index}__].variety}" is good for when you want to access an already existing set of objects in a collection. Its not really designed for doing selections by using wrapper objects IMO as it creates unnecessary boilerplate code and is sort of a hack.
Consider this simple example, a Person can select Drinks they like. Note: Constructors, Getters and setters are omitted for clarity. Also, these objects are normally stored in a database but I am using in memory arrays to explain the concept.
public class Person {
private Long id;
private List<Drink> drinks;
}
public class Drink {
private Long id;
private String name;
}
Spring controllers
The main thing here is that we are storing the Person in the Model so we can bind it to the form within th:object.
Secondly, the selectableDrinks are the drinks a person can select on the UI.
#GetMapping("/drinks")
public String getDrinks(Model model) {
Person person = new Person(30L);
// ud normally get these from the database.
List<Drink> selectableDrinks = Arrays.asList(
new Drink(1L, "coke"),
new Drink(2L, "fanta"),
new Drink(3L, "sprite")
);
model.addAttribute("person", person);
model.addAttribute("selectableDrinks", selectableDrinks);
return "templates/drinks";
}
#PostMapping("/drinks")
public String postDrinks(#ModelAttribute("person") Person person) {
// person.drinks will contain only the selected drinks
System.out.println(person);
return "templates/drinks";
}
Template code
Pay close attention to the li loop and how selectableDrinks is used to get all possible drinks that can be selected.
The checkbox th:field really expands to person.drinks since th:object is bound to Person and *{drinks} simply is the shortcut to referring to a property on the Person object. You can think of this as just telling spring/thymeleaf that any selected Drinks are going to be put into the ArrayList at location person.drinks.
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" >
<body>
<div class="ui top attached segment">
<div class="ui top attached label">Drink demo</div>
<form class="ui form" th:action="#{/drinks}" method="post" th:object="${person}">
<ul>
<li th:each="drink : ${selectableDrinks}">
<div class="ui checkbox">
<input type="checkbox" th:field="*{drinks}" th:value="${drink.id}">
<label th:text="${drink.name}"></label>
</div>
</li>
</ul>
<div class="field">
<button class="ui button" type="submit">Submit</button>
</div>
</form>
</div>
</body>
</html>
Any way...the secret sauce is using th:value=${drinks.id}. This relies on spring converters. When the form is posted, spring will try recreate a Person and to do this it needs to know how to convert any selected drink.id strings into the actual Drink type. Note: If you did th:value${drinks} the value key in the checkbox html would be the toString() representation of a Drink which is not what you want, hence need to use the id!. If you are following along, all you need to do is create your own converter if one isn't already created.
Without a converter you will receive an error like
Failed to convert property value of type 'java.lang.String' to required type 'java.util.List' for property 'drinks'
You can turn on logging in application.properties to see the errors in detail.
logging.level.org.springframework.web=TRACE
This just means spring doesn't know how to convert a string id representing a drink.id into a Drink. The below is an example of a Converter that fixes this issue. Normally you would inject a repository in get access the database.
#Component
public class DrinkConverter implements Converter<String, Drink> {
#Override
public Drink convert(String id) {
System.out.println("Trying to convert id=" + id + " into a drink");
int parsedId = Integer.parseInt(id);
List<Drink> selectableDrinks = Arrays.asList(
new Drink(1L, "coke"),
new Drink(2L, "fanta"),
new Drink(3L, "sprite")
);
int index = parsedId - 1;
return selectableDrinks.get(index);
}
}
If an entity has a corresponding spring data repository, spring automatically creates the converters and will handle fetching the entity when an id is provided (string id seems to be fine too so spring does some additional conversions there by the looks). This is really cool but can be confusing to understand at first.
When user click "All"(20k) from drop-down list of "List of users", Internet Explorer 10 falls in "Not Responding" state.
If user ignore that "Not Responding" state and keep waiting, full report is shown after approximately 4 minutes.
Trying this procedure on Firefox 26(supported) and Chrome 32(unsupported) and I don't encounter it.
How to improve performance AjaxFallbackDefaultDataTable?
private WebMarkupContainer createReportContainer()
{
Collections.addAll(numberRowsList, "50", "100", "1000", "All");
final WebMarkupContainer usersDiv = new WebMarkupContainer("usersDiv");
usersDropDown = new NoDefaultDropDownChoice<String>("usersDropdown", new Model<String>(String.valueOf(DEFAULT_ROWS_PER_PAGE)), numnerRowsList);
usersDropDown.add(new AjaxFormComponentUpdatingBehavior("onchange")
{
private static final long serialVersionUID = 1L;
#Override
protected void onUpdate(final AjaxRequestTarget target)
{
final int index = Integer.valueOf(usersDropDown.getValue());
try
{
rowsPerPage = Integer.valueOf(numberRowsList.get(index));
}
catch (final NumberFormatException ex)
{
rowsPerPage = usersTable.getRowCount();
}
catch (final Exception ex)
{
rowsPerPage = 0;
}
if (rowsPerPage < 1)
{
rowsPerPage = DEFAULT_ROWS_PER_PAGE;
}
usersTable.setRowsPerPage(rowsPerPage);
target.addComponent(usersTable);
}
});
usersDiv.add(usersDropDown);
final List<IColumn<ReportDisplayItem>> usersColumns = new ArrayList<IColumn<ReportDisplayItem>>();
usersColumns.add(new TextFilteredPropertyColumn<ReportDisplayItem, String>(new StringResourceModel("FirstName", this, null),
"firstName", "firstName"));
usersTable = new AjaxFallbackDefaultDataTable<ReportDisplayItem>("table", usersColumns, dataProvider, rowsPerPage);
usersDiv.add(usersTable)
HTML
<html>
<wicket:extend>
<div class="user-management">
<div wicket:id="navborder">
<div wicket:id="reportsDiv">
<div id="header-title" class="topheader">
<div class="topsubheader">
<p><wicket:message key="LiteralReports">Reports</wicket:message></p>
</div>
</div>
<div wicket:id="feedback" class="error-message"></div>
<div class="row row-auto">
<div class="column-1"></div>
<div class="column-2"><hr width="60%" align="left"></div>
</div>
<div class="section" wicket:id="usersDiv">
<div class="row section-header datatable-header">
<div class="column-1and2">
<wicket:message key="LiteralLocalUsers">Users (Local)</wicket:message>
<span class="shownumberofrecords">
<wicket:message key="LiteralDisplay">Display:</wicket:message>
<select wicket:id="usersDropdown"></select>
<wicket:message key="LiteralItems">items</wicket:message>
</span>
</div>
</div>
<div class="row row-auto">
<div class="column-1and2">
<!-- <table class="dataview" cellspacing="0" wicket:id="table">[table]</table> -->
<form wicket:id="FilterForm" id="FilterForm">
<input type="hidden" name="tracker" wicket:id="focus-tracker"/>
<table class="dataview" cellspacing="0" wicket:id="table">[table]</table>
<span wicket:id="focus-restore">[call to focus restore script]</span>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</wicket:extend>
</html>