Struts2 with Nested Iterator dynamic column and rows - struts2

I want to display grid in Struts2 which include dynamic rows and columns, it also provide that data should be save in database.
so i have created one list for columns and other map for that values in one bean.
I have included code also.
My bean looks like
public class Annexure{
private List<String> columnsList = new ArrayList<String>(1);
private Map<String,List<String>> columnsValues = new HashMap<String,List<String>>(1);
... setter/getter methods
}
Action class
package com.eks.ias.web.annexure.action;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.eks.ias.web.annexure.vo.Annexure;
import com.opensymphony.xwork2.ActionSupport;
public class AnnexureAction extends ActionSupport {
private static final long serialVersionUID = -8819437646232339486L;
private Annexure annexure = new Annexure();
public String execute()throws Exception {
List<String> columnsList = new ArrayList<String>();
columnsList.add("STNNo");
columnsList.add("EAN");
columnsList.add("ArticleCode");
annexure.setColumnsList(columnsList);
annexure.setTotalColumns(3);
annexure.setName("Stock Pending for Inward in SAP");
annexure.setDescription("Details of all merchandise physically received");
annexure.setSiteName("XXX");
Map<String,List<String>> columnsValues = new HashMap<String,List<String>>();
columnsValues.put("0", columnsList);
columnsValues.put("1", columnsList);
annexure.setColumnsValues(columnsValues);
return SUCCESS;
}
public void setAnnexure(Annexure annexure) {
this.annexure = annexure;
}
public Annexure getAnnexure() {
return annexure;
}
}
JSP page
<s:iterator value="annexure.columnsValues" status="rows">
<tr>
<s:iterator value="annexure.columnsList" status="columns">
<td><s:textfield name="annexure.columnsValues[%{#rows.index}][%{#columns.index}]" theme="simple"/></td>
</s:iterator>
</tr>
</s:iterator>
Html code generated looks like
<tr>
<td>
<input type="text" name="annexure.columnsValues[0][0]" value="STNNo"
id="annexure_annexure_columnsValues_0__0_"/>
</td>
<td>
<input type="text" name="annexure.columnsValues[0][1]" value="EAN"
id="annexure_annexure_columnsValues_0__1_"/>
</td>
<td>
<input type="text" name="annexure.columnsValues[0][2]" value="ArticleCode"
id="annexure_annexure_columnsValues_0__2_"/>
</td>
</tr>
when i submit data then i am not able to get those data in action
I am not able to understand the problem if data populate then data should get in action also.

Sorry I don't have enough rep to post a comment, and I'm not sure whether I understand your problem fully. However, there is one thing that I noted with the row index.
shouldn't this
<s:textfield name="annexure.columnsValues[%{#rows.index}][%{#columns.index}]" theme="simple"/>
rather be
<s:textfield name="annexure.columnsValues['%{#rows.index}'][%{#columns.index}]" theme="simple"/>
Note the single quotes. This should set the string key correctly in the map.

Code will be something like this
<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>
Here's the complete example on Nested Iterator in Struts2

Related

How to make a CREATE view & controller method for a model that has a list field?

I have these 2 models:
public class Invoice
{
public string InvoiceID {get; set; }
public List<InvoiceElement> InvoiceElements {get; set;}
[...other fields...]
}
public class InvoiceElement
{
public string InvoiceElementID {get; set; }
[ForeignKey("Invoice")]
public string InvoiceID { get; set; }
public virtual Invoice Invoice { get; set; }
public string Item {get; set;}
[...other fields...]
}
I am unable to make a CREATE view for new Invoices that lets me add InvoiceElements.
I want to have a "CurrentInvoiceElements" table where to dinamically add rows.
Just trying to making it simple. You can use the name attribute (the attribute that asp.net uses for modal binding) and post a list along with other properties of the class. You can use javaScript to append new elements to your form. Using the above modals you've provided, I have written a simple example using simple jQuery functions.
Razor View:
<button class="btn btn-success" id="add_btn">Add Invoice Element</button>
#using (#Html.BeginForm("SaveInvoice", "Invoice", FormMethod.Post))
{
<!--Other modal attributes inputs goes here -->
<!--You can use normal html helper extensions -->
<table id="element_table">
<thead>
<tr>
<td>Element Id</td>
<td>Item</td>
</tr>
</thead>
<tbody>
<tr>
<td><input name="invoice.InvoiceElements[0].Item" id="InvoiceElements[0].Item" /></td>
<td><input name="invoice.InvoiceElements[0].InvoiceElementID" id="InvoiceElements[0].InvoiceElementID" /></td>
</tr>
</tbody>
</table>
<input type="submit" />
}
JavaScript:
<script type="text/javascript">
$("#add_btn").on('click', function (e) {
var table = $("#element_table");
var idx = $(table).find("tbody>tr").length;
var htmlToAppend = `<tr>
<td><input name="invoice.InvoiceElements[${idx}].Item" id="InvoiceElements[${idx}].Item" /></td>
<td><input name="invoice.InvoiceElements[${idx}].InvoiceElementID" id="InvoiceElements[${idx}].InvoiceElementID" /></td>
</tr>`;
$(table).find("tbody").append(htmlToAppend);
});
</script>
Controller / Action:
[HttpPost]
public ActionResult SaveInvoice(Invoice invoice)
{
/* your logic here */
if(ModelState.IsValid)
_invoiceBusiness.SaveInvoice(invoice);
return View();
}
Please make sure the variable name in the parameter of the action method matches the name used in the name attribute of the input tag. i.e. name = "invoice.***" public ActionResult SaveInvoice(Invoice invoice)
I followed this solution: https://github.com/danludwig/BeginCollectionItem some years ago, and worked fine.
If I'm not mistaken, at the time I managed to do it using only the HTML Helper: HtmlPrefixScopeExtensions. Then just make sure the name you give on your View when you do Html.BeginCollectionItem("name") is exactly the same as your collection on your ViewModel.
That's for binding back to the controller.
With this, you can dynamically add rows using AJAX per example.
I hope it's clear enough. If you don't understand it I may make a Github repository with this.

Set color for even rows using <s:set> - Struts2 [duplicate]

This question already has an answer here:
Using <s:iterator> to populate a dynamic arraylist over multiple columns
(1 answer)
Closed 4 years ago.
I want to give dynamic color/style for even rows of the table. Below is my code but it is not giving me required output.
<table>
<s:iterator value="allMonths" status="incr">
<style>
.evenRow{background-color: yellow;}
.oddRow{background-color: orange;}
</style>
<tr>
<s:if test="#incr.count%2 ==0">
<s:set var="clr" value="evenRow"></s:set>
</s:if>
<s:else>
<s:set var="clr" value="oddRow"></s:set>
</s:else>
<td class="%{#clr}">Month:</font> <s:property/></td>
<%-- <td class="%{clr}">Month:</font> <s:property/></td> --%>
<%-- <td class="clr">Month:</font> <s:property/></td> --%>
</tr>
</s:iterator>
</table>
index.jsp
<%# taglib prefix="s" uri="/struts-tags" %>
<html>
<body>
<s:form action="clickAction">
<s:textfield name="beanobject.uname" label="Enter Username" /><br>
<s:textfield name="beanobject.age" label="Enter Age" /><br>
<s:textfield name="beanobject.sex" label="sex" /><br>
<s:submit value="Submit" align="center" />
<s:submit value="Clear" align="center" action="clearAction"/>
</s:form>
</body>
</html>
MyAction.java
---imports---
public class MyAction extends ActionSupport{
private MyBean beanobject;
private List<String> allMonths;
//Getters & Setters
public String execute(){
System.out.println("execute");
return SUCCESS;
}
public String click()
{
allMonths = new ArrayList<String>();
allMonths.add("Jan");
allMonths.add("Feb");
allMonths.add("Mar");
return SUCCESS;
}
public String clear(){
return SUCCESS;
}
}
I also tried using "bgcolor" attribute but with no success. What am I missing?
Provided required code. I am just hardcoding values in list in ActionClass
You're assuming JSP understands OGNL; it does not. JSP understands JSP EL.
OGNL is strictly within S2 custom tags, not generic JSP.

Multiple outcomes in implicit navigation with JSF 2.0

Please I am missing a point here. How can I handle a request with multiple outcomes in JSF 2.0 using the implicit navigation
For example using the explicit navigation I can write the following:
<navigation-rule>
<from-view-id>/products/DeleteItem.xhtml</from-view-id>
<navigation-case>
<from-outcome>itemDeleted</from-outcome>
<to-view-id>/products/Success.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>itemNotExist</from-outcome>
<to-view-id>/products/Failure.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
However How can I achieve the same thing with the implicit navigation, I have tried the following :
<h:link value="Delete a Product" outcome="Success" />
only one case is mentioned but I want to forward either to Success.xhtml or Failure.xhtml depending on the outcome.
Here a some additional information for the implicit navigation.
Managed bean:
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
#ManagedBean
#RequestScoped
public class DeletionBean {
#EJB
private app.session.ProductFacade ejbProductFacade;
private int id=0;
public DeletionBean() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String deleteProduct(){
String result;
result=ejbProductFacade.deleteItem(id);
if(result.equals("success"))
{
status="Successfully performed"; //msg to be shown
return "product/DeleteItem";
}
else return "product/Failure";
}
}
Form:
<h:form>
<table border="1" width="5" cellspacing="1" cellpadding="1">
<tr>
<td> Product ID </td>
<td>
<h:inputText id="pid" size="10" value="#{deletionBean.id}" title="Product ID" required="true" requiredMessage="Product ID required"/>
</td>
</tr>
<tr>
<td colspan="2">
<h:commandLink value="Delete" action="#{deletionBean.deleteProduct}" />
</td>
</tr>
</table>
</h:form>
Current error message:
Unable to find matching navigation case with from-view-id '/product/DeleteItem.xhtml' for action '#{deletionBean.deleteProduct}' with outcome 'product/Failure'
Supposing you'll perform an action method to delete your product, you should make your method return the desired outcome. Using JSF 2, there's no need of using outcome ids anymore, even you can declare them in your faces-config.xml file, JSF ties a provided outcome with a specific page:
<h:commandLink action="#{bean.deleteProduct(product)}"
value="Delete product" />
public String deleteProduct(Product p){
//Try to delete the product and return "products/xxx",
//which will be converted to "/contextname/products/xxx.xhtml"
try{
daoService.delete(p);
return "/products/Success";
catch(Exception e){
return "/products/Failure";
}
}
That will POST the server and behaves like an HTML form submit button (that's why it's called commandLink). Nevertheless if you want just to perform a GET to an specific view, you can use its view id in the outcome:
<h:link value="Go to success doing nothing"
outcome="/products/Success" />
See also:
Implicit navigation in JSF 2

Trouble with lifecycle in JSF 2.0 and selectManyListbox

I am trying to make a simple admin page for managing users list. The jsf code looks like this:
<h:selectOneMenu id="selectUser" value="#{adminBean.user_id}" valueChangeListener="#{adminBean.userSelected}" >
<f:selectItems value="#{adminBean.myModelUsersValues}" />
<a4j:ajax event="valueChange" render="login password privilege_list" execute="#this"/>
</h:selectOneMenu >
<table>
<tr>
<td><h:outputLabel styleClass="LabelStyle" value="login: "/></td>
</tr>
<tr>
<td>
<h:inputText id="login" value="#{adminBean.login}"/>
</td>
<td>
<h:message for="login" style="color:red"/>
</td>
</tr>
<tr>
<td><h:outputLabel styleClass="LabelStyle" value="password: "/></td>
</tr>
<tr>
<td>
<h:inputText id="password" value="#{adminBean.password}"/>
</td>
<td>
<h:message for="password" style="color:red"/>
</td>
</tr>
<tr>
<td><h:outputLabel styleClass="LabelStyle" value="privilege list: "/></td>
</tr>
<tr>
<td>
<h:selectManyListbox id="privilege_list" value="#{adminBean.privilegeList}">
<f:selectItems value="#{adminBean.privilegeValues}" />
</h:selectManyListbox >
</td>
<td>
<h:message for="privilege_list" style="color:red"/>
</td>
</tr>
</table>
<br/>
<h:commandButton id="addButton" value="Add" action="#{adminBean.addUser}" styleClass="ButtonStyle"/>
<h:commandButton id="deleteButton" value="Delete" action="#{adminBean.deleteUser}" styleClass="ButtonStyle"/>
<h:commandButton id="clearButton" value="Clear" action="#{adminBean.clear}" styleClass="ButtonStyle"/>
The problem is that when the page loads, all the items are empty. Now When I click on 'add' button I have discovered that the valueChangeListener="#{adminBean.userSelected}" runs, which replaces my privilege list with the ones from the first user. The same is when I use the clear button - all fields are empty, but when I click on the add button again, the list is the one from the first user (and only the list - no other input texts). I tried adding immediate="true" to the add button and that solves this problem, but off course then all the values I put into input text are not passed through to the adminBean.addUser action method. My bean is viewscoped (I needed to use it because of the validation error on selectManyListBox). Here is the Java code (the addUser method so far only sends a logger method and checks i login exists, and if sth was selected on the priv. list):
#ManagedBean(name="adminBean")
#ViewScoped
public class AdminBean {
private String user_id ="";
private String login ="";
private String password ="";
private ArrayList<String> privilegeList = new ArrayList<String>();
private User model = new User();
private TreeMap<String, User> usersValuesBackendMap = new TreeMap<String, User>();
private TreeMap<String, String> privilegesValues = new TreeMap<String, String>();
private TreeMap<String, String> myModelUsersValues = new TreeMap<String, String>();
...
#javax.annotation.PostConstruct
public void init()
{
usersValuesBackendMap = queryDAO.getAllUsers();
for (Map.Entry<String, User> usr : usersValuesBackendMap.entrySet()) {
myModelUsersValues.put(usr.getValue().getLogin(), usr.getKey() );
}
privilegesValues = queryDAO.getFullPrivilegeList();
user_id = "";
}
public void userSelected(ValueChangeEvent event){
String newValue = event.getNewValue().toString();
User user = usersValuesBackendMap.get(newValue);
login = user.getLogin();
password = user.getPassword();
privilegesValues.clear();
for (String privilege: user.getPrivilegeValues() ){
privilegesValues.put(privilege, privilege);
}
}
public String clear(){
user_id ="";
login ="";
password ="";
privilegesValues = queryDAO.getFullPrivilegeList();
return "";
}
Interestingly I added immediate="true" to the clearing method and then sth. opposite happens - the list is OK but the inputTexts are filled.
Some facts:
The valueChangeListener runs when !oldValue.equals(newValue).
The valueChangeListener is not a client side event listener. It's entirely server side.
The valueChangeListener is completely independent on whether it's an ajax or normal request. It's invoked on all types of requests, including the normal form submit.
You need <a4j:ajax listener> instead.
<h:selectOneMenu id="selectUser" value="#{adminBean.user_id}">
<f:selectItems value="#{adminBean.myModelUsersValues}" />
<a4j:ajax event="valueChange" listener="#{adminBean.userSelected}" render="login password privilege_list" execute="#this"/>
</h:selectOneMenu >
with
public void userSelected(AjaxBehaviorEvent event){
User user = usersValuesBackendMap.get(user_id);
// ...
}

Map multiple objects in to action class in 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>

Resources