How to display existing and newly created values in EL in Spring MVC? - spring

Only the last value of the model is displayed as EL, so the existing value is not displayed.
The BoardController code for that.
#RequestMapping("viewPage.do")
public String viewPage(HttpSession session,HttpServletRequest request, HttpServletResponse response,Model model,
#RequestParam(value="board_idx", defaultValue="0") int board_idx) throws IOException,IllegalStateException
{
// View comment list for this post
List<HashMap<String, Object>> commentList= bService.getBoardForComment(boardData.getBoard_idx());
// loop (comment.size)
for(int i = 0; i < commentList.size(); i++)
{
System.out.println("commentList Size : " + commentList.size());
//Saving the board_comm_idx value in the commentList to a variable
int board_comm_idx= (Integer) commentList.get(i).get("board_comm_idx");
//System.out.println("board_comm_idx : " + board_comm_idx);
// View comments in comments (viewed by board_idx, board_comm_idx)
List<HashMap<String, Object>> getCommentOfCommentList =
bService.getBoardCommentOfComment(board_idx, board_comm_idx);
model.addAttribute("cocList", getCommentOfCommentList);
System.out.println("GetCommentOfCommentList : " + getCommentOfCommentList);
System.out.println("cocList (Model) : " + model);
}
model.addAttribute("commentList", commentList); // CommentList Information
return "viewPage";
}
Here is the JSP code.
<c:forEach items="${cocList}" var="cocList">
<div class="commentList" style="margin-left:70px">
<div class="what">
<div class="nickname">
<span>
${cocList.board_c_of_c_writer}
</span>
</div>
<div class="writedatezone" style="margin-left: 715px;">
<span class="era">
<span class="glyphicon glyphicon-time enterReReply"></span>
<span class="commentWriteDate">
<fmt:formatDate value="${cocList.board_c_of_c_writeDate}" pattern="yyy-MM-dd"/>
</span>
</span>
</div>
<c:if test="${cocList.idx == idx}">
<div class="duzone">
<span class="deleteOrUpdateComment">
<a class="updateComment">수정</a>
<a class="deleteComment" onclick="deleteCoc(${cocList.board_idx},${cocList.board_c_of_c_idx})">삭제</a>
<input type="hidden" value="${cocList.board_comm_idx}">
<input type="hidden" value="${cocList.board_c_of_c}">
<input type="hidden" name="board_idx" value="${boardInformation.board_idx}">
</span>
</div>
</c:if>
</div>
<pre>${cocList.board_c_of_c_content}</pre>
</div>
</c:forEach>
First, it is the log of when you first commented on the comment.
GetCommentOfCommentList : [{board_idx=3, board_c_of_c_writer=웹개발자, board_c_of_c_content=Test, board_c_of_c_idx=8, board_comm_idx=5, idx=4, board_c_of_c_writeDate=2017-11-30 10:33:06.0}]
cocList (Model) : {cocList=[{board_idx=3, board_c_of_c_writer=웹개발자, board_c_of_c_content=Test, board_c_of_c_idx=8, board_comm_idx=5, idx=4, board_c_of_c_writeDate=2017-11-30 10:33:06.0}]}
After that, if you make a new comment,
GetCommentOfCommentList : [{board_idx=3, board_c_of_c_writer=웹개발자, board_c_of_c_content=Test, board_c_of_c_idx=8, board_comm_idx=5, idx=4, board_c_of_c_writeDate=2017-11-30 10:33:06.0}]
cocList (Model) : {cocList=[{board_idx=3, board_c_of_c_writer=웹개발자, board_c_of_c_content=Test, board_c_of_c_idx=8, board_comm_idx=5, idx=4, board_c_of_c_writeDate=2017-11-30 10:33:06.0}]}
GetCommentOfCommentList : []
cocList (Model) : {cocList=[]}
As above, when you create a new comment, the comment list will disappear from the existing comment.
The last cocList is an empty string because you have not commented on the comment.
I am convinced that EL is wrong. How do I fix the EL to solve my problem?

Even though you have the
<c:forEach items="${cocList}" var="cocList">
cocList is a reference your list (model attribute), but since you are assigning it to something else var="cocList" you are going to have unintended consequences, you should name your list item to something else.
<c:forEach items="${cocList}" var="item">
You should then be able to reference the item like ${item.blah}
Think of
<c:forEach items="${cocList}" var="cocList">
as
for (Object cocList : cocList) {
...
}
Which obviously is bad, instead you want:
<c:forEach items="${cocList}" var="item">
which is
for (Object item : cocList) {
...
}
I believe that should fix your problem.
For what it is worth as well, most likely, you are not using EL or SpEL, you are using JSTL at the moment. Normally the SpEL tags are going to look like spring:..., c:... normally references JSTL.

I solved this problem.
I added getCommentofCommentList to commentList instead of modifying the query statement.
As follows.
for(int i = 0; i < commentList.size(); i++)
{
System.out.println("commentList Size : " + commentList.size());
// commentList 에 담긴 board_comm_idx 값을 변수에 저장
int board_comm_idx= (Integer) commentList.get(i).get("board_comm_idx");
//System.out.println("board_comm_idx : " + board_comm_idx);
// 댓글에 댓글 조회 (board_idx , board_comm_idx으로 조회)
List<HashMap<String, Object>> getCommentOfCommentList =
bService.getBoardCommentOfComment(board_idx, board_comm_idx);
commentList.get(i).put("cocList", getCommentOfCommentList);
}
I added the following code.
commentList.get(i).put("cocList", getCommentOfCommentList)
as the result, It's very well work.

Related

How to show appropriate message on page with Thymeleaf if list is empty

I have some controller that is connected with some lists, now that list can be empty, I'm trying to provide a user message on page if list is empty. To be more clear, user can have wallet, inside that wallet user can create a transactions, because all of that I have a page Transactions now, if user still didnt create any transaction but visit that page, I want to show him message like You didnt create any transaction.
I found this example, and tried to apply on my problem, but so far it didnt work: How to check null and empty condition using Thymeleaf in one single operation?
This is my controller:
#GetMapping("/userTransactions/{user_id}")
public String getUserTransactions(#PathVariable("user_id") long user_id, TransactionGroup transactionGroup, Model model) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
UserDetailsImpl user = (UserDetailsImpl) authentication.getPrincipal();
long userId = user.getId();
model.addAttribute("userId", userId);
List<Transaction> transactions = transactionRepository.getTransactionsByUserId(user_id);
List<TransactionGroup> transactionByDate = new ArrayList<>();
List<Transaction> transOnSingleDate = new ArrayList<>();
boolean currDates = transactions.stream().findFirst().isPresent();
if (currDates) {
LocalDate currDate = transactions.get(0).getDate();
TransactionGroup transGroup = new TransactionGroup();
for (Transaction t : transactions) {
if (!currDate.isEqual(t.getDate())) {
transGroup.setDate(currDate);
transGroup.setTransactions(transOnSingleDate);
transactionByDate.add(transGroup);
transGroup = new TransactionGroup();
transOnSingleDate = new ArrayList<>();
}
transOnSingleDate.add(t);
currDate = t.getDate();
}
transGroup.setDate(currDate);
transGroup.setTransactions(transOnSingleDate);
transactionByDate.add(transGroup);
} else {
System.out.println("Empty");
model.addAttribute("transactionGroup", "NoData");
}
model.addAttribute("transactionGroup", transactionByDate);
return "transactions";
}
And that seems to work fine, I mean, if I didn't create a transaction, message System.out.println("Empty"); will be printed, but message on page is not displayed neither.
This is thymeleaf:
<div class="date" th:each="singleGroup : ${transactionGroup}">
<h1 th:text="${singleGroup .date}"></h1>
<div class="transaction" th:each="singleTrans : ${singleGroup.transactions}">
<h2>Amount: <span th:text="${singleTrans.amount}"></span></h2><br>
<h2>Note: <span th:text="${singleTrans.note}"></span></h2><br>
<h2>Wallet name: <span th:text="${singleTrans .walletName}"></span></h2><br>
<h2>Expense Category: <span th:text="${singleTrans .expenseCategories}"></span></h2><br>
<h2>IncomeCategory: <span th:text="${singleTrans .incomeCategories}"></span></h2>
<a class="card__link" th:href="#{/api/transaction/delete/{id}(id=${singleTrans.id})}"
th:data-confirm-delete="|Are you sure you want to delete ${singleTrans.walletName} wallet?|"
onclick="if (!confirm(this.getAttribute('data-confirm-delete'))) return false">Delete</a>
<hr>
</div>
<th:block th:if="${transactionGroup=='NoData'}"> No Data Found </th:block>
</div>
And here is the part: <th:block th:if="${transactionGroup=='NoData'}"> No Data Found </th:block> But its not displayed if list is empty
What I'm missing?
You can check if your list is empty like this
<div th:if="${#lists.isEmpty(transactionGroup)}"> No Data Found </div>
<div th:if="${#lists.size(transactionGroup) ==0}"> No Data Found </div>
In addition to that, your th:block is placed inside the list outer loop and it should be outside. That's why you're not seeing anything when the list is empty.

How to use spring mvc with thymeleaf to iterate over a list?

My goal is to cycle through a list of objects, some to be displayed on the screen, others to be passed into a form as an object of which I can define certain aspects and then return to the controller the object and attribute to be modified.
The problem with the following approach is that the object in the list is not passed correctly to the form and thus gives an error because it is trying to make changes to a non-existent object.
If, on the other hand, I try to pass it as an object via ModelAndView it obviously works but does not have all the characteristics of the object I passed via the list.
Controller
#GetMapping("/")
public ModelAndView home() throws IOException {
ModelAndView mv = new ModelAndView();
mv.setViewName("home");
List<Comics> allComics = cs.getAll();
mv.addObject("comics", allComics);
return mv;
}
#PostMapping("/update")
public ModelAndView update(Comics com, #RequestParam("attr") String attr) throws IOException {
ModelAndView mv = new ModelAndView();
com.setLastRead(attr);
cs.updateAttributes(com);
mv.setViewName("home");
List<Comics> allComics = cs.getAll();
mv.addObject("comics", allComics);
return mv;
}
home.html
<html xmlns:th="http://www.thymeleaf.org">
<tr th:each="comic : ${comics}">
<td th:text="${comic.title}"></td>
<td th:text="${comic.lastChapter}"></td>
<td>
<a th:href="${comic.lastChapterLink}" target="_blank"
role="button" class="btn btn-md btn-block btn-info"> Link
</a>
</td>
<td></td>
<td>
<form th:action="#{/update}" th:object="${comic}" method="post">
<input type="text" name="attr" id="attr"/>
<button type="submit">Sub</button>
</form>
</td>
</tr>
PS: I cut out the head of the html page because it was full of non-relevant CDNs
How can I integrate Spring MVC with Thymeleaf to achieve the result whereby passing a list of objects can be displayed on the screen and used for other purposes within the html page without throwing errors?
Obviously if you know of more efficient methods to achieve the result I'm listening; I only used this method because I didn't know of any others.
Thank you
Answer to #RafaeldaSilva:
I agree, but that does not solve the problem.
Let me explain: the attribute I am going to modify through the form already has its name to allow what you wrote.
But the object iterated through:
tr th:each="comic : ${comics}">
cannot be passed directly as input, as it is a value that is taken from a list and exists individually only in the html page.
One might think of passing it as hidden input, but in this case the result would be the same (I have tried):
<form th:action="#{/update}" th:object="${comic}" method="post">
<input type="hidden" value="${comic}" name="com"/>
<input type="text" name="attr" id="attr"/>
<button type="submit">Sub</button>
</form>
#PostMapping("/update")
public ModelAndView update(#RequestParam("com") Comics com, #RequestParam("attr") String attr) throws IOException {
ModelAndView mv = new ModelAndView();
com.setLastRead(attr);
System.out.println("Comic: " + com);
cs.updateAttributes(com);
mv.setViewName("home");
List<Comics> allComics = cs.getAll();
mv.addObject("comics", allComics);
return mv;
}
Error:
[org.springframework.web.bind.MissingServletRequestParameterException: Required request parameter 'com' for method parameter type Comics is present but converted to null]
try removing the type="hidden" to see what is present in this input, as I understand you are inserting an object by doing value="${comic}", this way the input should not send the value wanted..
change this: <input type="hidden" value="${comic}" name="com"/>
for this: <input type="text" value="${comic}" name="com"/>
so you can see what the form is sending to the controller, I believe it's the object's memory path, and not the data that exists in it.
in the input you must inform the attributes of the object, not the complete object..

Send parameter from Spring MVC controller to jsp code

I'm trying to send the variable average review from controller to view and show a number of stars equal with average review.
The problem is that I don't know how to loop through received variable and java code doesn't recognize that variable. I tried with a foreach from JSTL, but there is no list of objects; I want a classic for loop.
Here is my controller:
#RequestMapping(value = "/viewDetails", method = RequestMethod.GET)
public ModelAndView viewProductDetails(HttpServletRequest request) {
int productID = Integer.parseInt(request.getParameter("id"));
// irrelevant code goes here
double averageReview= reviewDAO.getAverageReview(productID);
modelAndView.addObject("averageReview",averageReview);
return modelAndView;
}
This is my view page, where I tried to loop:
<p>
<c:set var = "averageReview" scope = "session" value ="${averageReview}"/>
<% for(int i=0;i< ${averageReview}; i++){ %>
<span class="glyphicon glyphicon-star"></span>
<% } %>
</p>
How about this?
<c:forEach var = "i" begin = "0" end = "${averageReview}">
<span class="glyphicon glyphicon-star"></span>
</c:forEach>

How do I iterate over all my model attributes on my JSP page?

I'm using Spring 3.2.11.RELEASE with JBoss 7.1.3.Final and Java 6. I have this method in a controller
#RequestMapping(value = "/method", method = RequestMethod.GET)
public String myMethod(final Model model,
final HttpServletRequest request,
final HttpServletResponse response,
final Principal principal)
...
model.addAttribute("paramName", "paramValue");
Notice how I add attributes into my model. My question is, on the JSP page that this page serves, how do I iterate over all the attributes in my model and output them as HIDDEN input fields with the name of the INPUT being the attribute name and the value being what I inserted in the model using that attribute?
Edit: In response to the answer given, here was the output to the JSP solution. Note there are no model attributes in there.
<input type='hidden' name='javax.servlet.jsp.jspRequest' value='org.springframework.web.context.support.ContextExposingHttpServletRequest#7a0a4c3f'>
<input type='hidden' name='javax.servlet.jsp.jspPageContext' value='org.apache.jasper.runtime.PageContextImpl#3939794a'>
<input type='hidden' name='appVersion' value='???application.version???'>
<input type='hidden' name='javax.servlet.jsp.jspResponse' value='org.owasp.csrfguard.http.InterceptRedirectResponse#722033be'>
<input type='hidden' name='javax.servlet.jsp.jspApplication' value='io.undertow.servlet.spec.ServletContextImpl#14c1252c'>
<input type='hidden' name='org.apache.taglibs.standard.jsp.ImplicitObjects' value='javax.servlet.jsp.el.ImplicitObjectELResolver$ImplicitObjects#23c27a49'>
<input type='hidden' name='javax.servlet.jsp.jspOut' value='org.apache.jasper.runtime.JspWriterImpl#b01a1ba'>
<input type='hidden' name='javax.servlet.jsp.jspPage' value='org.apache.jsp.WEB_002dINF.views.lti.launch_jsp#1dcc48bf'>
<input type='hidden' name='javax.servlet.jsp.jspConfig' value='io.undertow.servlet.spec.ServletConfigImpl#3fd40806'>
Model attributes are "request scope" objects
you can do the following (I use JSTL):
<c:forEach items="${requestScope}" var="par">
<c:if test="${par.key.indexOf('attrName_') > -1}">
<li>${par.key} - ${par.value}</li>
</c:if>
</c:forEach>
Since with no filter you will have all the request scope objects, I filtered by the model attributes we wanted to check
I tested by using this code:
#RequestMapping(method = { RequestMethod.GET }, value = { "/*" })
public String renderPage(Model model) throws Exception
{
String requestedUrl = req.getRequestURI();
int indice = requestedUrl.lastIndexOf('/');
String pagina = requestedUrl.substring(indice + 1);
try
{
String usernameUtente = "default username utente";
if (StringUtils.hasText(getPrincipal()))
{
usernameUtente = getPrincipal();
}
model.addAttribute("usernameUtente", usernameUtente);
model.addAttribute("webDebug", webDebug);
for(int i = 0; i<10; i++)
{
model.addAttribute("attrName_"+i, "attrValue_"+i);
}
return pagina;
}
catch (Exception e)
{
String message = "Errore nell'erogazione della pagina " + pagina;
logger.error(message, e);
return "genericError";
}
}
And this is what I see as output (I omitted the not relevant prints but please note you'll print ALL the request scope objects:
attrName_0 - attrValue_0
attrName_1 - attrValue_1
attrName_2 - attrValue_2
attrName_3 - attrValue_3
attrName_4 - attrValue_4
attrName_5 - attrValue_5
attrName_6 - attrValue_6
attrName_7 - attrValue_7
attrName_8 - attrValue_8
attrName_9 - attrValue_9
I hope this can help
Angelo
For avoid headache with parameters added by Spring and Servlet container, it is better to use separate map for pass values into the model. Just use #ModelAttribute and Spring will create and add it to the model automatically:
#RequestMapping(value = "/method", method = RequestMethod.GET)
public String myMethod(final Model model, #ModelAttribute("map") HashMap<String, Object> map) {
map.put("paramName1", "value1");
map.put("paramName2", "value2");
//...and so on
}
Now you can iterate this map in JSP:
<c:forEach items="${map.keySet()}" var="key">
<input type="hidden" name="${key}" value="${map[key]}"/>
</c:forEach>
Also you can access to every item of map next way:
<c:out value="${map.paramName1}"/>
<c:out value="${map.paramName2}"/>
...
If you don't need some parameter to be iterable, add it into the original ModelMap istead of separate map.
In essence all you need is to itterate on all the page attributes. Depending on what you use on your jsp (scriptlets, jstl, or smthing like thymeleaf for html):
Scriptlet:
<form>
<% Session session = request.getSession();
Enumeration attributeNames = session.getAttributeNames();
while (attributeNames.hasMoreElements()) {
String name = attributeNames.nextElement();
String value = session.getAttribute(name);
%>
<input type='hidden' name="<% name %>" value="<% value %>">
<%
}
%>
</form>
JSTL:
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<h3>Page attributes:</h3>
<form>
<c:forEach items="${pageScope}" var="p">
<input type='hidden' name='${p.key}' value='${p.value}'>
</c:forEach>
</form>
Thymeleaf:
<form>
<input th:each="var : ${#vars}" type='hidden' name="${var.key}" value="${var.value}">
</form>
Simply you can iterate using foreach tag of Jstl.
<c:forEach items="${requestScope}" var="var">
<c:if test="${ !var.key.startsWith('javax.') && !var.key.startsWith('org.springframework')}">
<input type="hidden" name="${var.key}" value="${var.value}" />
</c:if>
</c:forEach>
Request attributes from spring framework and from Servlet do have prefixes, you don't need to add prefix to your request attributes.
Rather you can ignore all those attributes which have prefix "org.springframework" or "javax.".
You can try this:
#RequestMapping(value = "/method", method = RequestMethod.GET)
public String myMethod(final Model model,
final HttpServletRequest request,
final HttpServletResponse response,
final Principal principal)
...
//Create list for param names and another list for param values
List<String> paramNames = new ArrayList();
List<String> paramValues = new ArrayList();
paramNames.add("paramName1");
paramValues.add("paramValue1");
paramNames.add("paramName2");
paramValues.add("paramValue2");
//paramValue1 is the value corresponding to paramName1 and so on...
//add as many param names and values as you need
...
//Then add both lists to the model
model.addAttribute("paramNames", paramNames);
model.addAttribute("paramValues", paramValues);
Then in the JSP, you can iterate over paramNames list, and use the varStatus.index to get the index of current round of iteration and use it to pull the value of corresponding param value from the paramValues list. Like this -
<form id='f' name='myform' method='POST' action='/path/to/servlet'>
<c:forEach items="${paramNames}" var="paramName" varStatus="status">
<input type='hidden' name='${paramName}' value='${paramValues[status.index]}'>
</c:forEach>
</form>
You can add other input elements to the form as needed but the above should generate all the hidden input elements for each of your parameter that you set in the Model.

Spring, Thymleaf and lists of strings

Okay, I a wet-behind-the-ears newcomer to Spring and Thymleaf. I'm trying to do something so simple it should be a no-brainer. But I can't get it to work. The simple question is - how do you show a list of strings in an web page?
I have the following model
import java.util.List;
public class TestModel {
private List<String> list = null;
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public List<String> getList() { return list; }
public void setList(final List<String> list) {
this.list = list;
}
}
My web page contains the following:
<div th:if="${greeting.list != null}">
<h1>Result</h1>
<ul>
<th:block th:object="${greeting}" th:each="item : ${list}">
<li th:text="${item.name}">Item description here...</li>
</th:block>
</ul>
</div>
I added the ".name" to "item" only because I found a couple of examples where they had a list of strings and did something similar. But they had the ".name" on the object.
But it still doesn't work. The unordered list ends up empty. I.e. There isn't any list items inside the unordered tags.
What am I doing wrong? Pointers gladly accepted.
Since there's no example of filling model I supposed you put some strings into instance of TestModel's list field like this.
TestModel greeting= new TestModel();
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
model.addAttribute("greeting", greeting);
Then there are more errors in your Thymeleaf template example.
If you are using object selection through th:object you must at first place use asterix * to access object properties. Asterisk syntax evaluates expressions on selected objects instead of context variables map.
Object selection affects only children nodes in DOM.
In your example you want iterate over list of strings (List<String>) but you want to access property name which in fact don't exists on Java String object.
You must fix your Thymeleaf template in one way - see examples.
No object selection at all
<div th:if="${greeting.list != null}">
<h1>Result</h1>
<ul>
<li th:each="item : ${greeting.list}" th:text="${item}">Item description here...</li>
</ul>
</div>
Proper object selection
<div th:if="${greeting.list != null}">
<h1>Result</h1>
<ul>
<th:block th:object="${greeting}">
<li th:each="item : *{list}" th:text="${item}">Item description here...</li>
</th:block>
</ul>
</div>
<table th:object="${userList}" id="userTable" border="1">
<tr th:each="user :${userList}">
<td th:text="${user.getName()}"></td>
<td th:text="${user.getEmail()}"></td>
</tr>
</table>
Another example using table and Object. Might be useful to someone else.
In thymeleaf, if you are looking to create in a template a list of strings and compare it with a string, you can use the following structure.
th:if="${#lists.contains({'RO', 'UK', 'DE', 'BE'}, #session.getAttribute('country'))}"

Resources