It may be a silly qustion, but I still cannot find an answer to it.
My Spring Boot application looks something like this:
Model:
public class Company {
public static final String URL_COMPANY = "http://193.142.112.220:8337/companyList";
private Long iD;
private String companyName;
public static Map<Long, Object> companyMap;
public Long getiD() {
return iD;
}
public void setiD(Long iD) {
this.iD = iD;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
#Override
public String toString() {
return companyName;
}}
Controller:
#Controller
public class UrlController {
#GetMapping("/success")
public String show(Model model) {
HashMap<Long, Object> company = (HashMap<Long, Object>) Company.companyMap;
model.addAttribute("companyID", company);
return "success";
}
View:
<h1>All Companies:</h1>
<table border="1">
<tr>
<th>ID</th>
<th>Name</th>
</tr>
<tr th:each="mapEntry: ${companyID}">
<td th:text="${mapEntry.key}"></td>
<td th:text="${mapEntry.value}"></td>
</tr>
</table>
<a th:href="#{/}">Homepage</a>
</body>
</html>
So my goal is to display a table filled with Company ID's and Names. Even though, my model gets a map, I still can't see it in my browser. The table is empty.
</tr>
<tr th:each="mapEntry: {1=Tire Systems, 2=IT Enterprise, 3=Car Manufacture, 4=Electro Market}">
<td th:text=""></td>
<td th:text=""></td>
</tr>
This is what i get if i check the page source. So I clearly see, that map is loaded, but not displayed.
Moreover, a link with "homepage" does not work, and i am not sure why?
What am I missing? I am trying to fill a table with Companies, and then, using Id's of those companies, show materials attached to company via this ID. Can i use hyperlinks in table for Id's?
So you want to display a map. IF value of your map is a POJO Try something like followings
<tr><th>ID</th><th>Name</th></tr>
<tr th:each="mapEntry : ${companyID}">
<td th:text="${mapEntry.key}">keyvalue</td>
<td th:each="item : ${mapEntry.value}" th:text="${item.FIELD_NAME_OF_YOUR_POJO}">keyvalue</td>
</tr>
This should work. What i tried to show is, it is possible. The iteration depends on your data structure. If you have complex data structure iteration will change accordingly.
If value of your map is a primitive type or java reference type your current code should work. I have executed similar code like your's and it worked without any trouble. Please have a look -
HashMap<Long, Object> company = new HashMap<>();
company.put(1L, "Hello World");
model.addAttribute("companyID", company);
If value of your map is custom java type. Then follow the previous snippet.
Related
I'm trying to learn Spring and other related technologies during summer break by developing a simple web application, however Thymeleaf hinders my progress.
The program is based on two entity classes:
Invoice.java:
#Entity
public class Invoice {
#Id
private String invoiceId;
#NotNull
#DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate issueDate;
//getters and setters
}
TrasportOrder.java:
#Entity
public class TransportOrder {
#Id
private int number;
#NotNull
private BigDecimal value;
#ManyToOne
private Invoice invoice;
//getters and setters
}
I'm getting a form for adding invoices using a method from InvoiceController:
#GetMapping(path = "/add")
public String getForm(Model model) {
model.addAttribute("unusedOrders", service.getInvoiceOrders(null));
model.addAttribute("orders", new ArrayList<TransportOrder>());
model.addAttribute("invoice", new Invoice());
return "addInvoice";
}
unusedOrders is a list of orders that a user can choose from,
orders is a list that is meant to contain orders chosen by user
invoice is just an invoice that's being created in the form.
My form contains text and data inputs regarding the invoice, and then comes a multiple select for the orders:
<!-- I used to define th:object here and used th:field in the inputs, however changed it to use th:value everywhere -->
<form action="/invoices/add" method="post">
<table>
<tr>
<th>
Invoice ID:
</th>
<th>
<input type="text" th:value="${invoice.invoiceId}" name="invoiceId"/>
</th>
</tr>
<tr>
<!-- a few other inputs -->
</tr>
<tr>
<th>
Orders:
</th>
<th>
<!-- problem may lie here -->
<select id="orders" th:value="${orders}" multiple="multiple">
<option th:each="unusedOrder: ${unusedOrders}"
th:value="${unusedOrder.number}"
th:text="${unusedOrder}">Unused orders to choose from</option>
</select>
</th>
</tr>
</table>
<button type="submit">Next</button>
</form>
I've read Thymeleaf docs and forums, as well as several SO questions, but they still leave me confused about how does th:object, th:field, th:value and others work with forms, and especially with multiple select tag.
On submit, the form sends a POST request to a method in the same controller:
#PostMapping(path = "/add")
public String addInvoice(#ModelAttribute Invoice invoice,
BindingResult result,
#ModelAttribute("orders") ArrayList<TransportOrder> orders,
Model model) {
//invoice and orders saving logic, etc.
return "viewInvoices";
}
My problem is that invoice is being properly retrieved from the from and persisted in the database, but orders list stays empty. I expect it to be populated with orders chosen in the form. I don't know, if it's because of #ModelAttribute annotation (I also tried #RequestAttribute without success), Thymeleaf tags, or anything else.
Okay, so I decided to fight for the answer once more, and fortunately I stumbled upon an answer.
Based on this tutorial, I created an wrapper class ChosenOrdersDTO:
public class ChosenOrdersDTO {
private List<TransportOrder> chosenOrders;
//constructor, getters, setters...
}
I added it to the first model (changed the getForm() method as following):
model.addAttribute("chosenOrders", new ChosenOrdersDTO(new ArrayList<>()));
In the form I used the th:field tag, similarly to previous fields:
<select id="orders" th:field="${chosenOrders.chosenOrders}" multiple="multiple">
And in the second controller I was able to retrieve the list wrapped in the ChosenOrdersDTO class as a #ModelAttribute:
#PostMapping(path = "/add")
public String addInvoice(#ModelAttribute Invoice invoice,
BindingResult
#ModelAttribute ChosenOrdersDTO chosenOrders,
Model model)
I am using Spring Boot and Thymeleaf to create a single landing page for
my application. For this, I need to render a List of Host objects that all
contain a Container.
Here is the relevant code:
public class Container {
private String name;
private String baseUrl;
private String status;
public Container(String name, String baseUrl, String status) {
this.name = name;
this.baseUrl = baseUrl;
this.status = status;
}
public String getName() { return name; }
public String getBaseUrl() { return baseUrl; }
public String getStatus() { return status; }
}
public class Host {
private HashMap<String, Container> containers;
....
public List<Container> getContainers() {
return containers.values();
}
}
#RequestMapping("/")
public class IndexController {
#RequestMapping("/")
public String getIndex(Model model) {
model.addAttribute("hosts", hostRepository.getAllServers());
return "index";
}
}
Now I want to iterate over all servers and display the information about each Container in a table.
My Thymeleaf template looks like this:
<div class="panel panel-default" th:each="host : ${hosts}">
<div class="panel-heading">
<b th:text="${host.name}">Host X</b>
<div class="panel-body">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>URL</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr th:each="container : ${host.getContainers()}">
<!-- HERE IS THE PROBLEM -->
<td th:text="${container.name}">Service1</td>
<td th:text="${container.baseUrl}">domain.com/api/url</td>
<td th:text="${container.status}">RUNNING</td>
<!-- HERE ENDS THE PROBLEM -->
</tr>
</tbody>
</table>
</div>
</div>
</div>
My problem is the part where is access the container's properties (marked by the commentary).
Every time I get a SpringEL Exception. If I remove the th:text="${container.xy}" and replaces it with th:text="${container} a String version of the container is shown so I have access to the object and the loop it working properly. I also tried to replace the field access with getters (e.g. getStatus()) but it also does not work.
Thanks for your help. If you need more information, feel free to ask.
Setup:
Java 8
Spring Boot Starter Web
Thymeleaf
edit: The exception thrown is: nested exception is org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "container.name" (index:35) where index:35 is the first problematic line.
The toString() output when using ${container} is jenkins=com.my.app.Container#7552c269 and jenkins is the name attribute of the Container instance.
Solution It seemed that the nested loop was iterating over a Map instead of a List. Changing ${container.xy} to ${container.getValue().xy} solved the problem.
Solution
It seemes that the nested loop was iterating over a org.thymeleaf.util.EvaluationUtil$MapEntry instead of a List. Changing ${container.xy} to ${container.getValue().xy} solved the problem.
Bits learned along the way:
Override the toString() method to obtain formatted information about the object iterating over. In this case the output was key=value which altough value was expected. This gave a hint that the current object must be something else than a Container instance
Look at the stack trace of Thymeleaf (usually its a hint that something is null or not public)
Use getClass() on the current object during the iteration to check if something went wrong here
I am very new with SPRING MVC so really I dont know much about it as of the moment. I want to display all the fields in the database in a table view how do I do this?
in my controller
#RequestMapping(value = "task", method = RequestMethod.GET)
public String taskList(Map<String, Object> model) {
model.put("task", taskRepository.findAll());
return "/tasks/list";
}
my jsp:
<%#include file="/WEB-INF/views/includes/header.jsp"%>
<h4 class="form-header">${title}</h4>
<div class="forms col-md-12 bounceInDown mainContent" data-wow-delay="0.2s">
<table class="table table-striped">
<thead>
<tr>
<th>Task ID</th>
<th>Task Name</th>
<th>Task Description</th>
</tr>
</thead>
<tbody>
<c:if test="${empty task}">
<tr>
<td colspan="8">No task to Display</td>
</tr>
</c:if>
<c:if test="${not empty task}">
<c:forEach items="${tasks}" var="task">
<tr class="">
<td>${task.taskid}</td>
<td>${task.taskName}</td>
<td>${task.taskDescription}</td>
<td>
<fmt:message key="task.list.status.text.${task.status}" />
</td>
</tr>
</c:forEach>
</c:if>
</tbody>
</table>
</div>
<%#include file="/WEB-INF/views/includes/footer.jsp"%>
i dont have anything inside my taskRepository atm
For the starters:
#RequestMapping(value = "task", method = RequestMethod.GET)
public String taskList(Map<String, Object> model) {
model.put("task", taskRepository.findAll());
return "/tasks/list";
}
You should return some object you have created instead of String value. Let's asume you want to transfer two fields to you page lets name them field1 and field2. Now create your Data Transfer Object:
public class MyEntityDto{
private String filed1;
private String field2;
//Getter and setter method
.
.
.
}
Now your controller should look something like this:
#Autowired
SomeSevice someService;
#RequestMapping(value = "task", method = RequestMethod.GET)
#ResponseBody
public List<MyEntityDto> taskList(Map<String, Object> model) {
List<MyEntityDto> dtoList = someService.findALl();
return dtoList;
}
Your service from the other hand should look something like this:
#Service
public class SomeService(){
#Autowired
TaskRepository taskRepository;
public List<MyEntityDto> findAll(){
return assemblyTasks(taskRepository.findAll());//TODO implement method assemblyTasks
}
}
Notice that I put your repository usage into the service.This is the way it supposed to be done. You should use services in order to fetch data from your database and than you want to return your data using specificlly design for that purpose object - Data Transfer Object.
I leave the implementation of assemblyTask method to you. What you need to do there is to assign fields you want to pass from entity to view through your dto object. Generally you would want to have an assembler class for every DTO object but for the sake of simplicity I introduced the idea by using method. If you want to read more about DTO, view this post:
getting-value-of-invalid-field-after-methodargumentnotvalidexception
If you are completely new to the Spring world I recommend also find some basics web tutorials, for example here:
gonetoseries
I have a table listing registered activities with the possibility to delete each of them. The table looks like this:
<table class="table-striped table-condensed table-hover">
<tr>
<th>Date</th>
<th>Activity</th>
<th>Steps</th>
<th></th>
</tr>
#for(a <- activities) {
#helper.form(routes.DashboardController.deleteUA(a.id)) {
<tr>
<td style="width:30%">#a.date.format("dd MMM")</td>
<td style="width:30%">#a.activity.name</td>
<td style="width:30%">#a.steps</td>
<td>
<button type="submit" style="background-color:#FF6666"><i class="fa fa-trash-o"></i></button>
</td>
</tr>
}}
</table>
The method to delete the UserActivity object looks like this:
public static Result deleteUA(Long id) {
UserActivity.find.ref(id).delete();
return dashboard();
}
When the object is deleted the method will render the page again with the dashboard() method:
#Security.Authenticated(Secured.class)
public static Result dashboard() {
User user = User.find.byId(request().username());
return ok(dashboard.render(user, Tips.all(), 0.0 , getGoals(user), updateLeaderboards(), getRecentUA()));
}
When I submit the form and call on the deleteUA() the object is deleted as expected, but when I'm going to render the page with dashboard() I get this error:
[NullPointerException: The id is null]
from this line:
User user = User.find.byId(request().username());
If I go back to the dashboard by calling on the url (calling on the dashboard() method that way) everything seems fine and the deleted activity is gone from the list.
I can't understand why the deletion of an UserActivity object has anything to do with the finding of the user id in the session though. Any ideas?
Update! UserActivity looks like this:
package models;
import java.util.*;
import play.db.ebean.*;
import javax.persistence.*;
#Entity
public class UserActivity extends Model {
#Id
public Long id;
#ManyToOne
public User belongsTo;
#ManyToOne
public Activity activity;
public int intensity;
public double steps;
public Date date;
public static Finder<Long,UserActivity> find = new Finder<Long,UserActivity>(
Long.class, UserActivity.class
);
}
How about trying to use the redirect(...) method instead of just calling the controller's action.
I would assume that your routes file has some entry like this
GET /dashboard controllers.Application.dashboard()
Then in your deleteUA method you can write
public static Result deleteUA(Long id) {
UserActivity.find.ref(id).delete();
return redirect(controllers.Application.dashboard());
}
What I cannot figure out is how to select multiple input from multielect drop down to my Action.By using a collections instead of array.I have posted the both jsp code and actionform.
This is my jsp:
<table class="table-striped" style="width: 100%;">
<tr>
<th style="border: none;"><br><br><br><br><label class="control-label" >Grade</label></th>
<th style="border: none;">
<html:select name="GradeBoardConfigureForm" property="grade" multiple="">
<html:option value="">Grade List</html:option>
<html:optionsCollection name="GradeBoardConfigureForm" property="gradelist" label="grade" value="gradeid"/>
</html:select>
</th>
</tr>
</table>
FormBean:
public class GradeBoardConfigureForm extends ActionForm {
private String board;
private List grade;
private List gradelist;
private List boardlist;
public String getBoard() {
return board;
}
public void setBoard(String board) {
this.board = board;
}
public List getGradelist() {
gradelist = new ArrayList<>();
DAOFactory factory = HibernateDAOUtil.getDAOFactory();
GradeDao gradedao = factory.getGradeDao();
List<Academicgradeform> gradedaolist = gradedao.list();
gradelist.addAll(gradedaolist);
return gradelist;
}
public void setGradelist(List gradelist) {
this.gradelist = gradelist;
}
public List getBoardlist() {
boardlist = new ArrayList<>();
DAOFactory factory = HibernateDAOUtil.getDAOFactory();
BoardDao boarddao = factory.getBoardDAO();
List<Academicboardform> boarddaolist = boarddao.list();
boardlist.addAll(boarddaolist);
return boardlist;
}
public void setBoardlist(List boardlist) {
this.boardlist = boardlist;
}
public List getGrade() {
return grade;
}
public void setGrade(List grade) {
this.grade = grade;
}
}
As far as I know it is not directly possible and is not Struts1 philosophy.
A form bean should only be a piece of code that helps to transfer data between the controller (a singleton, so it is stateless) and a view. It is not intended to be a domain object.
So your GradeBoardConfigureForm is a good example of you should not be done. You mix DAO in a form bean. It will be hard to write and hard to test and will lead to poorly structured code. And your problem lays here. If you had a domain object distinct from your form bean, the domain object would have a List, and the DAO layer whould bind the list to the database. And the controller (Action in stuts1) would copy the list to an Array in the form bean, pass it to the view, and in submit phase will use a populated Array according to your needs.
But I have 3 strong advices for you :
Struts1 is deprecated (according to official Apache Struts page), do not use it except for maintaining existing code - Struts2 or Spring MVC are current alternatives
if you are a beginner, start by following existing tutorials
before coding, carefully design your program structure. If you do not understand what I mean here, read (again if you already had a look at it) this document linked at the bottom of Apache strus1 page : Understanding JavaServer Pages Model 2 architecture.