I've got a List of Products obtained from a database query.
In each Product there is a Date_in and a Date_out variable of Calendar type.
What I want to do is pass these Products to a jsp view, so that I can build a table with all their informations. To do so I need to convert the Calendar dates to a more suitable format (like a String), and I've got an utility for this.
My problem is: How can I pass each converted date_in/date_out to the request.setAttribute, for each Product in the List?
Here's what I've got.
in ProductAction:
List<Product> products = service.viewActiveProducts();
for(Product product: products) {
String date_inToString = DateConversionUtility.calendarDateToString(product.getDate_in());
String date_outToString = DateConversionUtility.calendarDateToString(product.getDate_out());
}
request.setAttribute("products", products);
in the jsp:
<table>
<tr class="index">
<td>Id</td>
<td>Date IN</td>
<td>Date OUT</td>
</tr>
<c:forEach items="${requestScope.products}" var="product">
<tr>
<td>${product.oid}</td>
<td>${product.date_in}</td>
<td>${product.date_out}</td>
</tr>
</c:forEach>
</table>
(Right now product.date_in and product.date_out are in a Calendar format. I want them in a String format).
You can create a custom JSTL function and use it directly in your JSP EL tags.
<td>${product.oid}</td>
<td>${nspace:formatDate(product.date_in)}</td>
<td>${nspace:formatDate(product.date_out)}</td>
Where nspace is the xml namespace assigned to your library and formatDate your method. A couple of links:
http://www.noppanit.com/how-to-create-a-custom-function-for-jstl/
http://anshulsood2006.blogspot.com.es/2012/03/creating-custom-functions-in-jsp-using.html
You can pass entire list to jsp by request.setAttribute(String,Object);
List<Product> products = service.viewActiveProducts();
request.setAttribute("products", products);
Your Product class should be implementing Serializable interface
To get your properties date_in and date_out in string format, convert them into string and have two more properties in Product class with names say dateInString and dateOutString and set their values like
for(Product product: products) {
product.setDateInString(DateConversionUtility.calendarDateToString(product.getDate_in()));
product.setDateOutString(DateConversionUtility.calendarDateToString(product.getDate_out()));
}
In jsp, you can user JSTL to get the properties or field of Products object iterating list
<c:forEach items="${products}" var="product">
<tr>
<td>${product.oid}</td>
<td>${product.dateInString}</td>
<td>${product.dateOutString }</td>
</tr>
</c:forEach>
Make a getDate_inString() and getDate_outString methods in Product bean that will print what you want. Then, in the jsp you can use ${product.date_inString}
Related
The goal: adding multiple table rows dynamically based on user inputs and catch the data through controller.
What I have so far(all simplified):
POJO:
public class Item(){
String price;
String weight;
getters and setters...
}
public class ItemForm(){
List<Item> items;
getter and setter...
}
JSP:
<form:form action="/create" method="POST" modelAttribute="itemForm">
<table>
<tr>
<td><input type='text' name='price'/></td>
<td><input type='text' name='weight'/></td>
</tr>
</table>
<c:forEach items="${itemForm.items}" var="item" varStatus="status">
<tr>
<td align="center">${status.count}</td>
<td><input name="items[${status.index}].price" value="${item.price}" /></td>
<td><input name="items[${status.index}].weight" value="${item.weight}" /></td>
</tr>
</c:forEach>
</form:form>
Controller:
private List<Item> items = new ArrayList<>();
#RequestMapping(value = "/create", method = RequestMethod.POST)
public String saveMultipleRows(#ModelAttribute("itemForm") ItemForm itemForm) {
items = itemForm.getItems();
if(items != null && items.size() > 0){
System.out.println("The list is not null!");
}
System.out.println("didn't get into the if statement");
return null;
}
I skipped the Javascript on adding table rows, if you think that have anything to do with this question, I will update my post and put the Javascript code.
The idea is to create a ItemForm class that contains a list of Item object, and in the JSP using JSTL c:foreach to save all the data from users to the list. And in my controller, if the list is not empty, I simply want to print out a message so that I know the list is not empty. But now if I run the program, it prints out "didn't get into the if statement".
So the problem I am currently having is the list is empty, that means I am not able to save the user input data to the list. Can anyone help me and let me know where I did wrong?
Below is the corrected code
since you have item in scope you can directly access the props of Item,You dont need to explicitly declare the index
<input name="${item.price}" value="${item.price}" />
<input name ="${item.weight}" value="${item.weight}" />
I have my response Object as which contains getter and setters for the Map -
public class DataResponse {
private Map<String, List<String>> attributes = new LinkedHashMap<String, List<String>>();
public Map<String, List<String>> getAttributes() {
return attributes;
}
public void setAttributes(Map<String, List<String>> attributes) {
this.attributes = attributes;
}
}
In the above object, I have a Map of String and List<String>. In the map, keys are my table header and the value in the map is the table data for that header.
Suppose if this is the value in the map -
FirstName is the Key in the same Map
DAVID, RON, HELLO are the values in the map as the List for that key.
Similarly,
LastName is the Key in the same Map
JOHN, PETER, TOM are the values in the map as the List for the `LastName` key.
Then my Table should look like this
FirstName LastName
David JOHN
RON PETER
HELLO TOM
I need to generate my above table dynamically as am passing my dataResponse object to my JSP page as mentioned below -
DataResponse dataResponse = some_code_here;
req.setAttribute("data", dataResponse);
WebUtil.forward(req, resp, this, "/admin/test.jsp");
And below is my table in JSP in which I am using my above object to generate the table in the above format
<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="1" style="text-align: center;">
<TR>
<c:forEach var="h" items="${data.attributes}">
<TH>${h.key}</TH>
</c:forEach>
</TR>
//iterate again
<c:forEach var="h" items="${data.attributes}">
//h.value is ArrayList so we can iterate with c:forEach
<c:forEach var="headers" items="${h.value}">
<TR>
<TD>${headers}</TD>
</TR>
</c:forEach>
</c:forEach>
</TABLE>
But somehow my tables are not getting shown in the way I am trying to show in my above example. All the keys are getting shown properly in the Table Headers but all the values are getting shown only in first column.
And size of the list will be same for all the keys.
Any thought how this can be done?
UPDATE:-
<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="1" style="text-align: center;">
<TR>
<c:forEach var="h" items="${data.attributes}">
<TH>${h.key}</TH>
</c:forEach>
</TR>
//iterate again
<c:forEach var="h" items="${data.attributes}">
<TR>
<c:forEach var="headers" items="${h.value}">
<TD>${headers}</TD>
</c:forEach>
</TR>
</c:forEach>
</TABLE>
This gives me -
FirstName LastName
David RON HELLO
JOHN PETER TOM
<%
// Create an ArrayList with test data
ArrayList list = new ArrayList();
Map person1 = new HashMap();
person1.put("name", "A");
person1.put("lastname", "A1";
list.add(person1);
Map person2 = new HashMap();
person2.put("name", "B");
person2.put("lastname", "B1");
list.add(person2);
Map person3 = new HashMap();
person3.put("name", "C");
person3.put("lastname", "");
list.add(person3);
pageContext.setAttribute("persons", list);
%>
<html>
<head>
<title>Search result: persons</title>
</head>
<body bgcolor="white">
Here are all persons matching your search critera:
<table>
<TH>Name</th>
<TH>Id</th>
<c:forEach items="${persons}" var="current">
<tr>
<td><c:out value="${current.name}" /><td>
<td><c:out value="${current.lastname}" /><td>
</tr>
</c:forEach>
</table>
This approach is easier and suggested.
If u still want to stick with your code, you can use jsp tags instead of jstl tags to achieve your goal as follow
<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="1" style="text-align: center;">
<TR>
<c:forEach var="h" items="${data.attributes}">
<TH>${h.key}</TH>
</c:forEach>
</TR>
//use jsp scriplets to acces both list simultaneoulsy
<% List data= request.getAttribute("data")==null?null:(List) request.getAttribute("data");
List Names=data.get(0);
List LastNames=data.get(1);
for(int i=0;i<Names.length();i++){ %>
<td><%=Names.get(i)%></td><td><%=LastNames.get(i)%></td>
<%
}
%>
I am using the following data structure to store JDBC results in a Servlet controller prior to displaying in a JSP view using JSTL.
TreeMap
- TreeMap
- String[]
Four columns of data are returned per row.
"CATEGORY","OSDIRECTORY","FILENAME","DESCRIPTION"
"CATEGORY","OSDIRECTORY","FILENAME","DESCRIPTION"
"CATEGORY","OSDIRECTORY","FILENAME","DESCRIPTION"
etc.
The goal is to store the results in the data structure as
Category
- FILENAME
- OSDIRECTORY
- DESCRIPTION
And to display the final results in the View as
Category A
Hyperlink
Hyperlink
Hyperlink
Category B
Hyperlink
Hyperlink
etc.
Relevant Servlet Controller Code Snippet
...
TreeMap treeMap = new TreeMap();
rs = stmt.executeQuery(query);
// Gather raw data
while(rs.next()){
if(!treeMap.containsKey(rs.getString("CATEGORY"))){
treeMap.put(rs.getString("CATEGORY"), new TreeMap());
}
String[] tmp = { rs.getString("OSDIRECTORY"), rs.getString("DESCRIPTION") };
((TreeMap)treeMap.get(rs.getString("CATEGORY"))).put(rs.getString("FILENAME"), tmp);
}
request.setAttribute("filemap", treeMap);
RequestDispatcher rd = request.getRequestDispatcher(VIEW_URL);
rd.forward(request, response);
...
Relevant JSP View JSTL Snippet
<c:forEach var="f" items="${filemap}">
<h1><c:out value="${f.key}"/></h1>
<c:forEach var="g" items="${filemap[f.key]}">
<a href="TBD">
<c:out value="${filemap[f.key][g.key][0]}"/>
<c:out value="${filemap[f.key][g.key][1]}"/>
</a>
</c:forEach>
</c:forEach>
I am wondering of there is a more concise way to express some of the JSTL expressions.
For example ${filemap[f.key][g.key][0]} just seems too verbose to me.
I would make objects to represent your data instead of using a map of maps. Since you have categories that contain files, make a category and file objects that look something like this:
public class Category {
private String name;
private List<DbFile> files = new ArrayList<DbFile>();
// getters & setters for each property go here
}
public class DbFile {
private String filename;
private String osDirectory;
private String description;
// getters & setters for each property go here
}
Then in your data access code you can iterate over the result set and build up a list of categories objects like this:
Map<String, Category> categoryMap = new TreeMap<String, Category>();
while(rs.next()){
String categoryName = rs.getString("CATEGORY");
Category category = categoryMap.get(categoryName);
if(!categoryMap.containsKey(categoryName)){
category = new Category();
category.setName(categoryName);
}
DbFile file = new DbFile();
file.setFilename(rs.getString("FILENAME"));
file.setOsDirectory(rs.getString("OSDIRECTORY"));
file.setDescription(rs.getString("FILENAME"));
category.getFiles().add(file);
categoryMap.put(categoryName, category);
}
request.setAttribute("categories", Arrays.asList(categoryMap.values()));
Then the code on your page is much more straightforward:
<c:forEach var="category" items="${categories}">
<h1><c:out value="${category.name}"/></h1>
<c:forEach var="file" items="${category.files}">
<a href="TBD">
<c:out value="${file.osDirectory}"/>
<c:out value="${file.description}"/>
</a>
</c:forEach>
</c:forEach>
After some later investigation and experimentation, I found this to be a cleaner approach than the JSTL posted in the question.
<c:forEach var="f" items="${filemap}">
<h1><c:out value="${f.key}"/></h1>
<c:forEach var="g" items="${f.value}">
<a href="TBD">
<c:out value="${g.value[0]}"/>
<c:out value="${g.value[1]}"/>
</a>
</c:forEach>
</c:forEach>
I am fetching a list of objects from DB. I would like to populate them in to a html table using velocity templates.
<table>
<thead>
<tr>
<td>$value1 </td>
<td>$value2 </td>
</tr>
</thead>
<tbody>
<!-- Iterate through the list (List<SomeObject>) and display them here, -->
</tbody>
</table>
For headers I am using the below code,
VelocityContext context = new VelocityContext();
context.put("value1", "text1");
context.put("value2", "text2");
I get data from objects as below,
List<SomeObject> obj = new ArrayList<SomeObject>();
obj.getItem1();
obj.getItem2();
All the individual items are Strings. How to populate the table body content?
Try the following:
<tbody>
#foreach( $obj in $objs )
<tr><td>$obj.Item1</td><td>$obj.Item2</td></tr>
#end
<tbody>
I assume that your list is put on the velocity context under the name objs and your SomeObject class has 2 fields: item1 and item2 with coresponding getters.
List<SomeObject> objs = ... //prepopulated
context.put("objs", objs);
See more on the velocity documentation.
It is possible to bind a List of String (List) and display them in a jsp in a combo box like this:
<form:select path="countryId">
<form:option value="" label="Please Select"></form:option>
<form:options items="${countryList}" itemValue="countryId" itemLabel="countryName"/>
</form:select>
I want this list to display in <td> or <form:input> like fields, not in combo box.
I am binding String list in model as
Map referenceData = new HashMap();
referenceData.put("OutputsList", Outputs);
In JSP I use
<c:forEach var="OutputsList" items="${Outputs}">
${OutputsList}
</c:forEach>
But list is not printed. What could be the reason?
do it that way.
<c:forEach var="country" items="${countryList}">
<tr>
<td>${country.countryId}</td>
<td>${country.countryName}</td>
</tr>
</c:forEach>
and on the server side use ModelAndView object
List<Country> countryList;
ModelAndView mv = new ModelAndView("index");
mv.addObject("country",countryList);
There was a wrong approach while using it in a jsp. From the code in question just swap OutputsList
Map referenceData = new HashMap();
referenceData.put("OutputsList", Outputs);
In JSP I use
<c:forEach var="item" items="${OutputsList}">
${item}
</c:forEach>
It will work.