I am using struts 2 and I have set up my code like this:
Action class:
public class OrderDetailAction extends BaseActionSupport {
private Collection<ShippingAddress> shippingAddressList;
<the getters and setters here>
private Collection<ShippingAddress> billingAddressList;
<the getters and setters here>
public String displayCreate() {
LOGGER.info("DISPLAY CREATE CALLED");
populateForiegnFields();
LOGGER.info("populate foreign fields done calling");
return SUCCESS;
}
private void populateForiegnFields(){
LOGGER.info("ENTERED POPULATE FOREIGN FIELDS");
ShippingAddressService shippingAddressService = ServiceFactory.getInstance().getShippingAddressService();
shippingAddressList = shippingAddressService.getShippingAddresss();
if(shippingAddressList == null) {
LOGGER.info("shipping address list IS NULL");
} else {
LOGGER.info("shipping address list IS NOT NULL. CONTENTS:");
}
getSession().put("shippingAddressList", shippingAddressList);
billingAddressList = shippingAddressService.getShippingAddresss();
if(billingAddressList == null) {
LOGGER.info("billingAddressList IS NULL");
} else {
LOGGER.info("billingAddressList IS NOT NULL. CONTENTS:");
}
getSession().put("billingAddressList", billingAddressList);
}
And the snippet of my Create Order Detail JSP is:
<s:form>
<div class="form-group">some other fields</div>
<div class="form-group">
<s:select label="SHIPPINGADDRESSID" list="shippingAddressList" listKey="ID" listValue="ID" name="shippingAddress" ></s:select>
</div>
<div class="form-group">
<s:select label="BILLINGADDRESSID" list="billingAddressList" listKey="ID" listValue="ID" name="billingAddress" ></s:select>
</div>
<input class="btn btn-success" type="submit" name="action:createOrderDetail" value="submit" id="displayCreateOrderDetail_createOrderDetail"/>
<input class="btn btn-default" type="submit" name="action:getOrderDetails" value="cancel" id="displayOrderDetail_getOrderDetails" />
</s:form>
The error I am getting is this:
tag 'select', field 'list', name 'shippingAddress': The requested list
key 'shippingAddressList' could not be resolved as a
collection/array/map/enumeration/iterator type
But what confuses me, especially after I looked up this error on other posts
people suggested that the shippingAddressList may never have been instantiated but when I checked the log files that I wrote above, it was not null and at one point, I also logged the values from shippingAddressList.
Are there other reasons I could be getting this error?
Thank you in advance.
First Remove Collection and instead use List.
private List<ShippingAddress> shippingAddressList;
<the getters and setters here>
private List<ShippingAddress> billingAddressList;
<the getters and setters here>
I am not sure why you are putting these two list on session. They will already be on value stack.
I believe your ShippingAddress class have ID property.
Oh boy so here is the error I was making. To make sure people don't run into this headache:
my getters and setters for my shippingAddressList and my billingAddressList were as follows:
get**s**hippingAddressList(); set**s**hippingAddressList()
and
get**b**illingAddressList(); set**b**illingAddressList()
The issue here was that the casing was not proper. Because it was not proper camel case, struts was never able to call the getters and setters.
Related
I need choose values from one array and assign it to other array. Using Spring Thymeleaf. No idea how retrieve these choosed values.
My classes:
#Entity
public class Collaborator {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Size (min=3, max=32)
private String name;
#NotNull
#ManyToOne (cascade = CascadeType.ALL)
private Role role;
public Collaborator() {}...
#Entity
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Size(min = 3, max = 99)
private String name;
public Role() {}....
My controllers:
#RequestMapping("/project_collaborators/{projectId}")
public String projectCollaborators(#PathVariable Long projectId, Model model) {
Project project = mProjectService.findById(projectId);
List<Collaborator> allCollaborators = mCollaboratorService.findAll();
List<Collaborator> assignments = new ArrayList<>();
if (project.getRolesNeeded()!=null) {
for (int i=0;i<project.getRolesNeeded().size();i++) {
assignments.add(new Collaborator("Unassigned", project.getRolesNeeded().get(i)));
assignments.get(i).setId((long) 0);
}
}
model.addAttribute("assignments", assignments);
model.addAttribute("allCollaborators", allCollaborators);
model.addAttribute("project", project);
return "project_collaborators";
}
#RequestMapping(value = "/project_collaborators/{projectId}", method = RequestMethod.POST)
public String projectCollaboratorsPost(#ModelAttribute Project project, #PathVariable Long projectId, Model model) {
Project p = mProjectService.findById(projectId);
//mProjectService.save(project);
return "redirect:/project_detail/{projectId}";
}
And template:
<form th:action="#{'/project_collaborators/' + ${project.id}}" method="post" th:object="${project}">
<label th:text="'Edit Collaborators: ' + ${project.name}">Edit Collaborators: Website Project</label>
<ul class="checkbox-list">
<li th:each="a : ${assignments}">
<span th:text="${a.role.name}" class="primary">Developer</span>
<div class="custom-select">
<span class="dropdown-arrow"></span>
<select th:field="${a.id}">
<option th:each="collaborator : ${allCollaborators}" th:value="${collaborator.id}" th:text="${collaborator.name}">Michael Pemulis</option>
</select>
</div>
</li>
</ul>
<div class="actions">
<input type="submit" value="Save" class="button"/>
Cancel
</div>
</form>
As you can see I want to let user choose for each role (roleNeeded) any collaborator from (allCollaborators) and keep that assigns in List (assignments).
And I get error message:
ava.lang.IllegalStateException: Neither BindingResult nor plain target
object for bean name 'a' available as request attribute
So question is: how to solve it, assign values from one array to another in template and retrieve that values in my controller.
The cause of the exception
The IllegalStateException you are getting is because th:field="${a.id}" in your select element must be related to the form th:object="${project}" element; the th:field attribute must refer to an actual field in the project instance (also you need to write th:field="*{fieldName}"). That should fix the exception you are getting, but will not solve your entire problem as the second part of it is related to how to make the values get into your controller, which I will explain next.
Sending the values to your controller
To get the values into your controller you will need to make a few changes. As I don't really know the code of your Project class, I will change a few things so you will be able to figure it out how to adapt this simple example to your particular case.
First, I understand you want to make a relation like the following in your form:
Role1 => CollaboratorA
Role2 => CollaboratorB
Your controller needs to receive a list and in order to receive this information we need two classes:
The class which will be storing the individual element data, mapping a role id with the collaborator id:
public class RoleCollaborator {
private Long roleId;
private Long collaboratorId;
public Long getRoleId() {
return roleId;
}
public void setRoleId(Long roleId) {
this.roleId = roleId;
}
public Long getCollaboratorId() {
return collaboratorId;
}
public void setCollaboratorId(Long collaboratorId) {
this.collaboratorId = collaboratorId;
}
}
A wrapper class to store a list of individual mappings:
public class RolesCollaborators {
private List<RoleCollaborator> rolesCollaborators;
public List<RoleCollaborator> getRolesCollaborators() {
return rolesCollaborators;
}
public void setRolesCollaborators(List<RoleCollaborator> rolesCollaborators) {
this.rolesCollaborators = rolesCollaborators;
}
}
The next thing to do is change your controllers where you have two methods, one that handles GET requests and another one which handles the POST requests and so, receives your form data.
In the GET one:
public String projectCollaborators(#PathVariable Long projectId, Model model) {
(... your code ...)
model.addAttribute("project", project);
// Add the next line to add the "rolesCollaborators" instance
model.addAttribute("rolesCollaborators", new RolesCollaborators());
return "project_collaborators";
}
As you can see, we added a line that will be used by the thymeleaf template. Right now is a wrapper of an empty list of roles and collaborators, but you can add values if you need to edit existing mappings instead of adding new ones.
In the POST one:
// We changed the #ModelAttribute from Project to RolesCollaborators
public String projectCollaboratorsPost(#ModelAttribute RolesCollaborators rolesCollaborators, #PathVariable Long projectId, Model model) {
(... your code ...)
}
At this point, your controller is prepared to receive the information sent from your form, that we also need to modify.
<form th:action="#{'/project_collaborators/' + ${project.id}}" method="post" th:object="${rolesCollaborators}">
<label th:text="'Edit Collaborators: ' + ${project.name}">Edit Collaborators: Website Project</label>
<ul class="checkbox-list">
<li th:each="a, stat : ${assignments}">
<span th:text="${a.role.name}" class="primary">Developer</span>
<div class="custom-select">
<input type="hidden" th:id="rolesCollaborators[__${stat.index}__].roleId" th:name="rolesCollaborators[__${stat.index}__].roleId" th:value="${a.role.id}" />
<span class="dropdown-arrow"></span>
<select th:field="*{rolesCollaborators[__${stat.index}__].collaboratorId}">
<option th:each="collaborator : ${allCollaborators}" th:value="${collaborator.id}" th:text="${collaborator.name}">Michael Pemulis</option>
</select>
</div>
</li>
</ul>
<div class="actions">
<input type="submit" value="Save" class="button"/>
Cancel
</div>
</form>
Here there are a few changes:
As I pointed out in The cause of the exception you need to change th:object="${project}" to th:object="${rolesCollaborators}", as rolesCollaborator is the instance name from where you will receive the values from your GET controller method and where you will be sending the values to your POST controller method.
I added a hidden input; this input will store the role id that will be send in association to the collaborator id the user picks from the interface using the select element. Take a look at the syntax used.
I changed the th:field value of your select element to refer to a field in the rollesCollaborators object we use in th:object="${rolesCollaborators}" form attribute. This will set the value of the collaborator in the RoleCollaborator element of the RolesCollaborators wrapped list.
With these changes your code will work. Of course, you can improve it with some other modifications, but I tried to not introduce more modifications to focus on your problem.
I'm trying to create a list of objects from form inputs. The objects are the same but their values may differ, it's essentially a menu.
I'm still getting to grips with Spring/Thymeleaf which is adding some level of complexity to what feels like a simple task.
I've a class for the menu, a simple POJO, there is then a list of these defined as a data member in the bean itself:
private ArrayList<GuestMenuOptions> guestMenus;
I've read many posts, tried many things and am on the verge of softly resting my head against the table.
I've had several errors, most of which either tell me that the list cannot be found or that the list is empty - it's currently in stable condition where the list, no matter what I try, will not be populated, even when I load in default values...unfortunately my debugger has died which is not helping.
Any help is appreciated. thank you
EntryController:
#RequestMapping(method=RequestMethod.GET, value="/")
public String indexPage(Model model) {
model.addAttribute("childMenuOptions", generateChildMenus());
//not sure if this is neccesary...
ArrayList<GuestMenuOptions>guestMenus = new ArrayList<>();
GuestMenuOptions ad1 = new GuestMenuOptions();
GuestMenuOptions ad2 = new GuestMenuOptions();
guestMenus.add(ad1);
guestMenus.add(ad2);
GuestContactBean ctb = new GuestContactBean();
ctb.setGuestMenus(guestMenus);
model.addAttribute("guestContactBean", ctb);
model.addAttribute("formBackingBean", new FormBackingBean());
return "index";
}
Form:
<form modelAttribute="guestBean" class="contact_form" name="rsvp" role="form" th:object="${formBackingBean}" th:action="#{/sendRsvp}" method="post">
<div class="row">
<div class="form-group">
<select name="ad1Starter" id="starterMealAdult1">
<option value="!!!">-Starter-</option>
<option th:field="${guestContactBean.guestMenus[0].starter}" th:each="entry : ${adultMenuOptions.get('starter').entrySet()}" th:value="${entry.key}" th:text="${entry.value}">
</option>
</select>
</div>
<input type="submit"guest name="submit" class="btn default-btn btn-block" value="Send RSVP">
RequestController:
#RequestMapping(value = "/sendRsvp", method = RequestMethod.POST)
public String sendRsvp(#ModelAttribute("guestContactBean") GuestContactBean guestContactBean,
#ModelAttribute("guestMenus") ArrayList<GuestMenuOptions>menus,
BindingResult result) throws MessagingException {
smtpMailSender.send(guestContactBean);
return "thanksMessage";
}
Beans:
FormBacking is POJO with no reference to the menus at all.
GuestMenuOptions is the same with just starter, desert members
guestContactbean has not much more going on, basic fields with the addition of the list of GuestMenuOptions
private String numberOfAdults;
private String eventAttending;
private ArrayList<GuestMenuOptions> guestMenus;
public ArrayList<GuestMenuOptions> getGuestMenus() {
return guestMenus;
}
EDIT:
The field that populates the drop downs in working fine, it's declared as private Map<String, Map<String, String>> adultMenuOptions;
private Map<String, Map<String, String>> childMenuOptions;
they are then built in the controller so that each may have several options under 'starter', 'main' and desert' for example:
starter.put("salmon", "Smoked Salmon");
starter.put("pate", "Chicken Liver Pate");
this is then populating both the value and text of the dropdown.
If I could save the state of this Map and pass it back to the controller instead, that would also be fine but I wasn't able to why then spawned the creation of the there wrapper list.
Please revisit http://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html#dropdownlist-selectors. It should be as simple as
class Animal {
int id;
String name;
}
then in your template:
<select th:field="*{animalId}">
<option th:each="animal : ${animals}"
th:value="${animal.id}"
th:text="${animal.name}">Wireframe</option>
</select>
I think your code is all over the place and you're mixing up menu selection with menu item types.
I have issues in binding enum values in hidden fields with spring mvc.
The scenario is the following: I have a form with a list of documents which are characterized by a type.
My form class is (please note how the constructor is made. It fills the documents list with empty documents whose type is taken from the DocumentType enumeration):
public class Form {
private List<Document> documents = new ArrayList<Document>();
//while creating this class, I instantiate a number
//of empty documents with a certain type attribute
public Form() {
super();
for(DocumentType type : DocumentType.values())
{
Document document = new Document();
document.setType(type);
documents.add(document);
}
}
}
my document class is:
public class Document{
private DocumentType type;
private String expirationDate
private URL link
//getters & setters
}
while DocumentType is an ordinary enumeration
public enum DocumentType {
TYPE_A("type a"),
TYPE_B("type b"),
TYPE_C("type c");
private String literal;
public String getLiteral() {
return literal;
}
private StateDocumentType(String literal) {
this.literal = literal;
}
}
I want to display a form of this kind (the rounded boxes are form fields):
so, in order to update the right document with the proper link and expiration date, I want to use an hidden field associated to every form input row.
So my jsp is the following:
<form:form role="form" id="form" commandName="stateForm" action="${formUrl }">
<c:forEach items="${form.documents }" var="document" varStatus="vs">
<div id="doc_${document.type }>
<label>${document.type.literal }</label>
<div class="input-group">
<form:hidden path="documents[${vs.index }].type"/>
<form:input type="text" path="documents[${vs.index }].link"/>
</div>
</div>
<!-- omitted the exp. date part -->
</c:forEach>
</form:form>
here's the problem: when the html page is requested and rendered, the hidden field "value" attribute is just empty, while I expected it is valorized with the proper enum value. Please note that the parent <div> id is properly valorized. I really don't know why this doesn't work.
I am new to Struts2. I have a JSP page which shows a list of Rooms (where Room is a user-defined class). I need to send this entire list to the action class as a hidden field. The JSP code is as follows:
<form method="GET" action="reporting.action" >
<s:hidden name="roomsReport" value="%{allRooms}"/>
<s:textfield name="roomsR" value="%{allRooms}"/>
<s:submit name="action" style="width:220px;" value="Generate Report for Rooms" />
</form>
The textfield (used for testing) shows the address for the list (implying that its not null in the jsp page)
I am still unable to access it in my ReportingAction class using the following code:
System.out.println("xxx"+this.roomsReport.size());
System.out.println(this.roomsReport);
Both above print statements give 0 and [] respectively. I have the getters and setters for roomsReport as follows:
private List<Room> roomsReport;
public List<Room> getRoomsReport() {
return roomsReport;
}
public void setRoomsReport(List<Room> roomsReport) {
this.roomsReport = roomsReport;
}
Can anyone help ?
Finally I could pass the list to the jsp using session
The list is set in a session variable in jsp as follows:
<s:set name="roomsReport" value="%{allRooms}" scope="session" />
In the action class I can access it using the following code:
List<Room> roomsReport = (List<Room>)ActionContext.getContext().getSession().get("roomsReport");
I feel this should be exceedingly obvious, but so far I've failed to find an answer.
I want to have a list of strings (or an array of strings, I really don't care) get populated by form data in Struts2.
I've seen several examples of how to do indexed properties with beans, but wrapping a single string inside an object seems fairly silly.
So I have something like
public class Controller extends ActionSupport {
private List<String> strings = new ArrayList<String>();
public Controller() {
strings.add("1");
strings.add("2");
strings.add("3");
strings.add("4");
strings.add("5");
}
public String execute() throws Exception {
return ActionSupport.SUCCESS;
}
public List<String> getStrings() {
return strings;
}
public void setStrings(List<String> s) {
strings = s;
}
}
...
<s:iterator value="strings" status="stringStatus">
<s:textfield name="strings[%{#stringStatus.index}]" style="width: 5em" />
</s:iterator>
The form fields get populated with their initial values (e.g. 1, 2, etc), but the results are not properly posted back. setStrings is never called, but the values get set to empty strings.
Anybody have any idea what's going on? Thanks in advance!
I believe as you have it, your jsp code would render something like:
<input type="text" name="strings[0]" style="width: 5em" value="1"/>
<input type="text" name="strings[1]" style="width: 5em" value="2"/>
<input type="text" name="strings[2]" style="width: 5em" value="3"/>
...
Notice that the name of the field references are "strings[x]" where as you need the name to be just "strings". I would suggest something like:
<s:iterator value="strings" status="stringStatus">
<s:textfield name="strings" value="%{[0].toString()}" style="width: 5em" />
</s:iterator>
Not sure if the value attribute above may is correct, but I think something like this will get you the desired result.