Map multiple objects in to action class in struts2 - struts2

I have jsp page with multiple item objects. and gives action to Shop_shopCart.action.
inside action class there are cart object with multiple item objects. How it is possible to directly mapping from jsp to action class with multiple list objects.
Demo classes are given below.
<s:form action="Shop_shopCart.action">
// multiple items in cart object
</form>
class ShoppingAction extends ActionSupport{
Cart cart = new Cart();
//getters and setters
//action methods
String shopCart( ) {
// do some
}
}
class Cart{
List<Item> items = new ArrayList<Item>();
//getters and setters
}
class Item{
String name;
int id;
//getters and setters
}

See the type conversion collection and map support docs.
Nutshell: array or map notation (square brackets with an index or key value inside them) is the easiest way to submit a collection of objects in a form.

this example should help
<s:form action="saveaction" theme="css_xhtml">
<s:textfield name="carlist[0].cartid" label="Cart Id"/>
<s:textfield name="carlist[0].items[0].id" label="Item id"/>
<s:textfield name="carlist[0].items[0].name" label="Item Name"/>
<s:textfield name="carlist[1].cartid" label="Cart Id"/>
<s:textfield name="carlist[1].items[0].id" label="Item id"/>
<s:textfield name="carlist[1].items[0].name" label="Item Name"/>
<s:submit value="Click me to submit Cart List"/>
</s:form>

Related

update component in another xhtml form from Custom layout p:selectOneRadio not working

My xhtml is split in to Menu area (defaultMenu.xhtml) and Content area (defaultContent.xhtml).
The code for defaultMenu.xhtml is:
<h:form id="defaultmenuform">
<p:outputPanel id="menupanel" class="contain auto-fixed-center">
<p:panel id="pmenu" visible="#{phController.user.menuVisible}">
<table id="stutable">
<tr>
<td width="15%">
<p:outputLabel id="stuname" value="#{phController.phBean.studentName}" />
</td>
<td>
<p:tabMenu activeIndex="#{param.selectedtab}">
<p:menuitem value="Home" outcome="phome" icon="ui-icon-star">
<f:param name="selectedtab" value="0" />
</p:menuitem>
<p:menuitem value="Bank" outcome="bhome" icon="ui-icon-person">
<f:param name="selectedtab" value="1" />
</p:menuitem>
</p:tabMenu>
</td>
</tr>
</table>
</p:panel>
</p:outputPanel>
</h:form>
The defaultContent.xhtml actually displays the ph.xhtml content (as part of functional navigation) and the code is:
<ui:define name="content">
<f:event listener="#{phController.readPeople}" type="preRenderView">
</f:event>
<h:form id="form">
<p:selectOneRadio id="selstud" value="#{phController.phBean.ssSeq}" layout="custom">
<p:ajax update=":defaultmenuform:parentmenupanel :defaultmenuform:stuname" listener="#{phController.onChangePerson}"/>
<f:selectItems value="#{phController.selectStudents}" />
</p:selectOneRadio>
<div style="width: 300px; float:left;">
<p:dataGrid var="studentlist" value="#{phController.listStudent}" columns="1" rowIndexVar="stuindex">
<p:panel header="" style="text-align:left">
<h:panelGrid columns="1" style="width:100%">
<h:outputText value="#{studentlist.studentName}" />
<p:radioButton for=":form:selstud" itemIndex="#{stuindex}"/> Select
</h:panelGrid>
</p:panel>
</p:dataGrid>
</div>
</h:form>
</ui:define>
The code for backing bean is:
Map<String, Object> studentparam = new HashMap<>();
studentparam.put("studentSeq", phBean.getSsSeq());
lS = getBaseDAOService().readStudent("readStudent", studentparam);
phBean.setStudentName(lS.get(0).getStudentFirstName() + " " + lS.get(0).getStudentLastName());
As you can see, I am calling the onChangeStu method to display the selected Student Name in defaultMenu.xhtml. I am using Custom Layout p:selectOneRadio in ph.xhtml and onClick trying to update a p:outputLabel in defaultMenu.xhtml.
The backing bean method gets invoked successfully and the value is also set in variable phController.phBean.studentName, but the update is not working. I also checked using view source and the id is “:defaultmenuform:stuname”, I also tried updating the menu panel ":defaultmenuform:menupanel”, but none of this works.
Not sure how to resolve this. Please suggest.
Including the structure of all .xhtmls
<h:body id="entirePageBody">
<div id="page">
<ui:insert name="header" >
<ui:include src="/template/defaultHeader.xhtml" />
</ui:insert>
<ui:insert name="menu" >
<ui:include src="/template/defaultMenu.xhtml" />
</ui:insert>
<div id="content_div" class="auto-fixed-center">
<div id="content_div_padding" class="content-block">
<ui:insert name="content" >
<ui:include src="/template/defaultContent.xhtml" />
<ui:debug hotkey="z" />
</ui:insert>
</div>
</div>
<ui:insert name="footer" >
<ui:include src="/template/defaultFooter.xhtml" />
</ui:insert>
</div>
</h:body>
PhController.java:
public class PhController extends BaseController implements Serializable {
private List<Stud> listStudent;
private List selectStudents;
SelectItem option;
private PhBean phBean;
private Boolean menuVisible;
int counter = 0;
public PhController() {
phBean = new PhBean();
}
public void readPeople() {
listStudent = new ArrayList<Stud>();
listStudent.add(new Stud(1, "John Miller"));
listStudent.add(new Stud(2, "Scott Jackson"));
selectStudents = new ArrayList();
option = new SelectItem(listStudent.get(0).getStudentSeq(), "Select");
selectStudents.add(option);
option = new SelectItem(listStudent.get(1).getStudentSeq(), "Select");
selectStudents.add(option);
phBean.setSsSeq(String.valueOf(1));
phBean.setSelectedName(listStudent.get(0).getStudentName());
menuVisible = true;
}
public void onChangePerson() {
phBean.setSelectedName(listStudent.get(1).getStudentName());
}
// Getters and Setters
}
PhBean.java:
public class PhBean implements Serializable {
private String ssSeq;
private String studName; // Used to display the name in the Menu bar.
private String selectedName;
public PhBean() {
}
// Getters and Setters
}
I'd say that in the p:ajax in defaultContent.xhtml the list of components to be updated should be separated with spaces only, no commas - so try changing this:
update=":defaultmenuform:menupanel, :defaultmenuform:stuname"
to this:
update=":defaultmenuform:menupanel :defaultmenuform:stuname"
UPDATE
I played with this a bit more and may have found a clue - please add the following code to defaultmenuform:
<p:messages autoUpdate="true" showDetail="true" />
This should help us tracking the reason for failed validation (in case failing validation is the root cause for you - as I said, I have rather limited possibility to reproduce this issue).
Anyway, when I selected some item in p:selectOneRadio, an error message like this appeared:
Conversion Error setting value 'test001.Student#4110c95c' for 'null Converter'.
And the root cause was on this row:
<p:selectOneRadio id="selstud" value="#{phController.phBean.ssSeq}" layout="custom">
p:selectOneRadio expects only String to be passed as a value - and ssSeq is very likely of a different type. Try to change the way value is populated to ensure it is always String - maybe a different attribute of the phBean or simply a brand new String one.
NOTE: if this doesn't help, maybe you could update your question with very simplified example of how phController and phBean could look like if we are to test it.
UPDATE #2
So you have explained there is a problem that you want to call phController.readPeople every time the page is loaded/refreshed, but instead it gets loaded with each and every Ajax request, thus overwriting the values.
In your PhController (it is a bean, right? session scoped?) you could add something like this (omitted null checks for the sake of readability):
public void readPeopleOnGet() {
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();
HttpServletRequest req = (HttpServletRequest) ec.getRequest();
String reqMethod = req.getMethod();
if ("GET".equals(reqMethod)) {
readPeople();
}
}
With the above method you could keep this part of your defaultContext.xhtml in place, provided it is actually called (I assume so), just with the listener method changed:
<f:event listener="#{phController.readPeopleOnGet}" type="preRenderView">
</f:event>
The method readPeopleOnGet will still be called with every request to the page, but since Ajax requests are POST, it will only call readPeople when the page is loaded or refreshed as whole.
This may not be a "perfectly clean" solution, but seemed to work properly when I tested it.
UPDATE #3
Well, since you use PrimeFaces, it would be possible to identify Ajax call also this way:
public void readPeopleOnGet() {
RequestContext rc = RequestContext.getCurrentInstance();
if (!rc.isAjaxRequest()) {
readPeople();
}
}
But if I got your point from latest comments, you want to only run readPeople when the page is loaded for the very first time - so the following part could be even better for that.
You didn't answer if PhController is actually a bean, but I assume it is (there were no annotations visible from the code you posted). You may try making it #SessionScoped:
#ManagedBean
#SessionScoped
public class PhController extends BaseController implements Serializable {
// the rest of the PhController goes here...
Then you could add something like this to the PhController:
#PostConstruct
private void init() {
readPeople();
}
The annotation #PostConstruct ensures the method init is called exactly once after the bean was created. You can continue calling the method readPeople from other places as necessary, while removing both the <f:event ... /> and readPeopleOnGet as these will no longer be needed.

Struts 2 String to Double Conversion on ValueStack

I am new to Struts2 and following "Struts2 in Action". I have one question
My book says if you are using data-type different then String , you need to tell compiler that its no longer String and define property file named DataTransferTest-conversion.properties with Element-weights=java.lang.Double in case of List (i.e you are using Double type in your List).
However , when i did this practically , i didn't specified my property file. Still , its working. I don't know!! Why?
I am attaching my code layers. Please look
Action Class
public class ListExampleAction extends ActionSupport implements ModelDriven<ListExample> {
ListExample example = new ListExample();
private List<ListExample> listExample;
private Double x;
//Getters Setters//
#Action(value="/ListExample", results={
#Result(name="success",location="/listExampleDisplay.jsp"),
})
public String execute() throws Exception {
return "success";
}
Display Action Class( just to return jsp)
public class DisplayListExampleAction extends ActionSupport{
#Action(value="/ListExampleAction", results={
#Result(name="success",location="/listExample.jsp"),
})
public String execute() throws Exception {
return "success";
}
}
List Example Class for List Type
public class ListExample{
private String firstName;
private Double age;
//getter/setters//
}
listExample.jsp
<html>
<head></head>
<body>
<h1>Struts </h1>
<form action="ListExample">
// For List
<s:textfield name="listExample.firstName" label="User 1 Name"></s:textfield>
<s:textfield name="listExample.age" label="User 1 Age"></s:textfield>
// For x varaible in Action Class
<s:textfield name="x" label="x"></s:textfield>
<s:submit></s:submit>
</form>
</body>
</html>
listExampleDisplay.jsp
<html>
<head></head>
<body>
<h1>Struts</h1>
// For List
Users List =
<s:iterator value="listExample">
<s:property value="firstName" />
<s:property value="age" />
</s:iterator>
<br>
// For x varaible in Action Class
<s:property value="x"/>
</body>
</html>
Output:
UserAge =21
User Name = Saurabh
x = 21.0
Because the book is really old and covers only Struts 2.0.
Back then many types did require a conversion specification.
Later versions improved their ability to automatically convert between types through introspection.
IIRC that earlier book also didn't cover the annotations covered by some later books.

how to understad a form is submitted in struts actions

In struts2 actions the injection is used for setting the property values of an action class, so the properties are updated by form fields on the form submission. To discover which form is submitted I create a method calling isFormSubmitted() and in there I check a redundant property created only for this motive. The property is updated in a hidden field. But I find this workout so dirty! I think that there must be a better way to solve this problem.
What I do is:
<s:form name="form1">
<s:hidden name="submit" value="10" />
...other fields go here
</s:form>
in the action class I have getSubmit, setSubmit methods and the following method:
public boolean isFormSubmitted(){
return (submit == 10);
}
Your can call different action methods in your action, not only "execute" method. Just put the parameter with the name like "method:actionMethodName" in your request. Here is example:
public class MyAction extends ActionSupport {
public String execute() {
// Base code
return SUCCESS;
}
public String one() {
// Code one
return SUCCESS;
}
public String two() {
// Code two
return SUCCESS;
}
}
And here is jsp:
<s:form action="MyAction">
<input type="submit" value="Call execute"/>
<input type="submit" name="method:one" value="Call method one"/>
<input type="submit" name="method:two" value="Call method two"/>
</s:>
Or you can do it like this:
<s:form action="MyAction" name="form0">
<!-- call execute-->
</s:>
<s:form action="MyAction" name="form1">
<!-- call method one-->
<input type="hidden" name="method:one"/>
</s:>
<s:form action="MyAction" name="form2">
<!-- call method two-->
<input type="hidden" name="method:two"/>
</s:>
You should use <s:form> tag action attribute to submit to specific action.
<s:form action="action1">
...
</s:form>
<s:form action="action2">
...
</s:form>
See the <s:form> tag documentation: http://struts.apache.org/2.x/docs/form.html.
Update
Then just use separate actions for loading and saving the user.
you should do the submission of the form something like this.
<s:form name="form1">
<s:hidden name="submit" value="10" />
</s:form>
create a java script function
function onclick()
{
document.form1.submit();
}
then create getters & setters in action for hidden field
and in execute method
public String execute()
{
setSubmit(10);
return SUCCESS;
}
Update :
or
<s:a href="your_action"></a>

Struts 2 Populate select drop list

Hi i want to populate the select drop down list with some value. I use Struts 2, Tiles and JSP. I initialize my list in the Action class but i am still getting the following error :
Caused by: tag 'select', field 'list', name 'anneeResultat': The requested list key 'anneesResultatsList' could not be resolved as a collection/array/map/enumeration/iterator type. Example: people or people.{name} - [unknown location]
Here is my code in Action class:
private AnneeResultat anneeResultat;
private Map<String, String> anneesResultatsList = new HashMap<String, String>();
public Map<String,String> getAnneesResultatsList() {
this.anneesResultatsList.put("2005","2005");
this.anneesResultatsList.put("2006","2006");
this.anneesResultatsList.put("2007","2007");
this.anneesResultatsList.put("2008","2008");
this.anneesResultatsList.put("2009","2009");
this.anneesResultatsList.put("2010","2010");
this.anneesResultatsList.put("2011","2011");
return this.anneesResultatsList;
}
public void setAnneesResultatsList(Map<String,String> anneesResultatsList) {
this.anneesResultatsList = anneesResultatsList;
}
return SUCCESS;
}
My struts.xml file contains :
<action name="ChoixAxes" class="fr.si2m.occ.web.actions.ChoixAxesAction"
method="execute">
<result type="tiles">choixAxes.tiles</result>
</action>
My jsp is here:
<s:set name="theme" value="'xhtml'" scope="page" />
<s:form action="ChoisirAxes" name="choices" id="choices">
<s:select name="anneeResultat" label="Année de résultats" list="anneesResultatsList"></s:select>
<s:radio label="Listes nominatives" name="listesNominatives" list="#{'1':'Oui','2':'Non'}" value="2" />
<s:submit value="Calculer provisions" name="calculerProvisions"/>
<s:reset value="Annuler" />
<input type="button" value="Critères sauvegardés" id="criteresSauvegardes"/>
</s:form>
Some one could help me please ?
I have this problem since yersterday.
Prepare interceptor calls prepare() on actions which implement
Preparable. This interceptor is very useful for any situation where
you need to ensure some logic runs before the actual execute method
runs.
Your action should extend Preparable interceptor and override prepare() method, give the pre populated data.
Struts2 Prepare Interceptor
Put AnneesResultatsList in session
Map session=ActionContext.getScession();session.put("list",AnneesResultatsList );
<pre>
s:select name="anneeResultat" label="Année de résultats" list="%{#session.list}""></s:select>
</pre>
actually it is,
Map session = ActionContext.getcontext().getsession();
session.put("key",list);
<s:select list="%{#session.key}">

access data of struts2 nested iterator in action

<s:iterator var="parent" value="studentList">
<s:iterator var="child1" value="#parent.subjectList">
<s:property value="%{subjectName}" />:
<s:textfield id="subject" name="%parent.subject.id}" theme="simple" />
</s:iterator>
</s:iterator>
I have a jsp page with the above code. I have two lists i) studentList, ii) subjectList.
For each student there is a subjectList. Now I have to save the marks. How can I get these values in my action ? I am using Struts2.
Thanx in advance.
This is how the JSP code will look like:
<s:form action="saveaction" >
<s:iterator value="lstBean" id="lstBean" status="outerStat">
<s:textfield value="%{name}" name="lstBean[%{#outerStat.index}].name"/>
<s:textfield value="%{amt}" name="lstBean[%{#outerStat.index}].amt"/>
<s:textfield value="%{id}" name="lstBean[%{#outerStat.index}].id"/>
<s:iterator value="%{lstString}" status="myStat">
<s:textfield name="lstBean[%{#outerStat.index}].lstString[%{#myStat.index}]"/>
</s:iterator>
</s:iterator>
<s:submit value="Click me to submit lstBean"/>
</s:form>
Following is the bean(XBean) whose List is used in the JSP:
public class XBean
{
private ArrayList<String> lstString=new ArrayList<String>();
private String name;
private Double amt;
private Integer id;
//Getters and setters of fields
}
Now you can simply have a field lstBean with setters in your submitting action (saveaction) and hey you are done.

Resources