Hello,
Are you amazed by the
Struts2 Jquery Grid Showcase and trying to implement a Struts2 Jquery SubGrid but unable to figure out whats going wrong. Already spent hours figuring out the solution? Then you are at the right place.
This is a complete working example project in Netbeans 6.9 showcasing Struts2 Jquery SubGrid functionality, simply resolve the references and get going.
Description:
This is the structure of the Netbeans Project
Models- 1. Student (idstudent,name,address,email,phoneno)
2. StuTrans (idstudent,amount,desc)
As specified Student.java and StuTrans.java depicts the above mentioned structure as a POJO with getters/setters of attributes.
StudentDAO.java
package dao;
import java.util.ArrayList;
import java.util.List;
import model.Student;
public class StudentDAO {
private static List<Student> stuLst = new ArrayList<Student>();
static {
for (int i = 1; i < 30; i++) {
stuLst.add(new Student(i*10, " name " + i, "address " + i, "email " + i, 999999999 + i));
}
}
public static List<Student> buildList() {
return stuLst;
}
public static int count() {
return stuLst.size();
}
public static List<Student> find(int o, int q) {
return stuLst.subList(o, q);
}
public static void save(Student c) {
stuLst.add(c);
}
public static Student findById(Integer id) {
for (Student c : stuLst) {
if (c.getIdstudent() == id) {
return c;
}
}
return null;
}
public static void update(Student c) {
for (Student x : stuLst) {
if (x.getIdstudent() == c.getIdstudent()) {
x.setName(c.getName());
}
}
}
public static void delete(Student c) {
for (Student x : stuLst) {
if (x.getIdstudent() == c.getIdstudent()) {
stuLst.remove(x);
}
}
}
}
StuTransDAO.java
package dao;
import java.util.ArrayList;
import java.util.List;
import model.StuTrans;
public class StuTransDAO {
private static List<StuTrans> stuLst = new ArrayList<StuTrans>();
static {
for (int i = 1; i < 30; i++) {
for (int j = 1; j < 5; j++) {
stuLst.add(new StuTrans(i*10, i * Math.PI + 100, "desc " + i));
}
}
}
public static List<StuTrans> buildList() {
return stuLst;
}
public static int count() {
return stuLst.size();
}
public static List<StuTrans> find(int o, int q) {
return stuLst.subList(o, q);
}
public static void save(StuTrans c) {
stuLst.add(c);
}
public static List<StuTrans> findById(Integer id) {
List<StuTrans> temp=new ArrayList<StuTrans>();
for (StuTrans c : stuLst) {
if (c.getIdstudent() == id) {
temp.add(c);
}
}
return temp;
}
public static void update(StuTrans c) {
for (StuTrans x : stuLst) {
if (x.getIdstudent() == c.getIdstudent()) {
// x.setName(c.getName());
}
}
}
public static void delete(StuTrans c) {
for (StuTrans x : stuLst) {
if (x.getIdstudent() == c.getIdstudent()) {
stuLst.remove(x);
}
}
}
}
actTransaction.java & actStudent.java are almost similar in the fashion that their task is to supply JSON to be displayed in the JQGrid. Both of them returns the fore-mentioned POJOs as JSONs.
actStudent.java
package actions;
import com.opensymphony.xwork2.ActionSupport;
import dao.StudentDAO;
import java.util.ArrayList;
import java.util.List;
import model.Student;
public class actStudent extends ActionSupport {// get index row - i.e. user click to sort.
private String sidx;
// Search Field
private String searchField;
// The Search String
private String searchString;
// he Search Operation ['eq','ne','lt','le','gt','ge','bw','bn','in','ni','ew','en','cn','nc']
private String searchOper;
// Your Total Pages
private Integer total = 0;
//Your result List
private List<Student> gridModel;
private List<Student> myList;
//get how many rows we want to have into the grid - rowNum attribute in the grid
private Integer rows = 0;
//Get the requested page. By default grid sets this to 1.
private Integer page = 0;
// sorting order - asc or desc
private String sord;
private boolean loadonce = false;
public boolean getLoadonce() {
return loadonce;
}
public void setLoadonce(boolean loadonce) {
this.loadonce = loadonce;
}
public String getSearchField() {
return searchField;
}
public void setSearchField(String searchField) {
this.searchField = searchField;
}
public String getSearchOper() {
return searchOper;
}
public void setSearchOper(String searchOper) {
this.searchOper = searchOper;
}
public String getSearchString() {
return searchString;
}
public void setSearchString(String searchString) {
this.searchString = searchString;
}
public List<Student> getGridModel() {
return gridModel;
}
public void setGridModel(List<Student> gridModel) {
this.gridModel = gridModel;
}
public Integer getPage() {
return page;
}
public String getSidx() {
return sidx;
}
public void setSidx(String sidx) {
this.sidx = sidx;
}
public String getSord() {
return sord;
}
public void setSord(String sord) {
this.sord = sord;
}
public void setPage(Integer page) {
this.page = page;
}
public Integer getRecords() {
return records;
}
public void setRecords(Integer records) {
this.records = records;
if (this.records > 0 && this.rows > 0) {
this.total = (int) Math.ceil((double) this.records / (double) this.rows);
} else {
this.total = 0;
}
}
public Integer getTotal() {
return total;
}
public void setTotal(Integer total) {
this.total = total;
}
// All Record
private Integer records = 0;
public String execute() {
System.out.println("grid action execute called");
myList = StudentDAO.buildList();
setRecords(myList.size());
int to = (getRows() * getPage());
int from = to - getRows();
if (to > getRecords()) {
to = getRecords();
}
System.out.println("loadonce : " + loadonce);
if (loadonce) {
System.out.println("returned from loadonce");
setGridModel(myList);
} else {
if (searchString != null && searchOper != null) {
//All searches here....
} else {
setGridModel(StudentDAO.find(from, to));
}
}
total = (int) Math.ceil((double) getRecords() / (double) getRows());
return SUCCESS;
}
public Integer getRows() {
return rows;
}
public void setRows(Integer rows) {
this.rows = rows;
}
public String getJSON() {
return execute();
}
}
actTransaction.java
package actions;
import com.opensymphony.xwork2.ActionSupport;
import dao.StuTransDAO;
import java.util.List;
import model.StuTrans;
public class actTransaction extends ActionSupport {
private Integer records = 0;
private Integer id;
private String sidx;
// Search Field
private String searchField;
// The Search String
private String searchString;
// he Search Operation ['eq','ne','lt','le','gt','ge','bw','bn','in','ni','ew','en','cn','nc']
private String searchOper;
// Your Total Pages
private Integer total = 0;
//Your result List
private List<StuTrans> gridModel;
private List<StuTrans> myList;
//get how many rows we want to have into the grid - rowNum attribute in the grid
private Integer rows = 0;
//Get the requested page. By default grid sets this to 1.
private Integer page = 0;
// sorting order - asc or desc
private String sord;
private boolean loadonce = false;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
// public boolean getLoadonce() {
// return loadonce;
// }
public void setLoadonce(boolean loadonce) {
this.loadonce = loadonce;
}
public String getSearchField() {
return searchField;
}
public void setSearchField(String searchField) {
this.searchField = searchField;
}
public String getSearchOper() {
return searchOper;
}
public void setSearchOper(String searchOper) {
this.searchOper = searchOper;
}
public String getSearchString() {
return searchString;
}
public void setSearchString(String searchString) {
this.searchString = searchString;
}
public List<StuTrans> getGridModel() {
return gridModel;
}
public void setGridModel(List<StuTrans> gridModel) {
this.gridModel = gridModel;
}
public Integer getPage() {
return page;
}
public String getSidx() {
return sidx;
}
public void setSidx(String sidx) {
this.sidx = sidx;
}
public String getSord() {
return sord;
}
public void setSord(String sord) {
this.sord = sord;
}
public void setPage(Integer page) {
this.page = page;
}
public Integer getRecords() {
return records;
}
public void setRecords(Integer records) {
this.records = records;
if (this.records > 0 && this.rows > 0) {
this.total = (int) Math.ceil((double) this.records / (double) this.rows);
} else {
this.total = 0;
}
}
public Integer getTotal() {
return total;
}
public void setTotal(Integer total) {
this.total = total;
}
// All Record
public String execute() {
System.out.println("gridaction actTransaction called");
int to = rows * page;
if (id != null) {
gridModel = StuTransDAO.findById(id);
}
records = gridModel.size();
// Set to = max rows
if (to > records) {
to = records;
}
// Calculate total Pages
total = (int) Math.ceil((double) records / (double) rows);
return SUCCESS;
}
public Integer getRows() {
return rows;
}
public void setRows(Integer rows) {
this.rows = rows;
}
public String getJSON() {
return execute();
}
}
Now comes the very important struts.xml.
struts.xml
<!Doctype.... tag (as usual)
<struts>
<!-- Configuration for the default package. -->
<package name="default" extends="struts-default,json-default">
<action name="ntable" class="actions.actStudent">
<result name="success" type="json"/>
</action>
<action name="nstutrans" class="actions.actTransaction">
<result name="success" type="json"/>
</action>
</package>
</struts>
and following is the code for index.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<%@taglib prefix="sj" uri="/struts-jquery-tags" %>
<%@taglib prefix="sjg" uri="/struts-jquery-grid-tags" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<sj:head jqueryui="true" jquerytheme="start"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Struts2-Jquery SubGrid</title>
</head>
<body>
<s:url id="remoteurl" action="ntable"/>
<s:url id="nremoteurl" action="nstutrans"/>
<sjg:grid
id="gridtable"
caption="Student Entry"
dataType="json"
href="%{remoteurl}"
pager="true"
gridModel="gridModel"
rowList="5,10,15,20"
rowNum="5"
rownumbers="true"
>
<sjg:grid id="nsubgrid"
subGridUrl="%{nremoteurl}"
gridModel="gridModel"
rowNum="-1"
footerrow="false"
userDataOnFooter="false"
>
<sjg:gridColumn name="idstudent" title="StudentId" sortable="false"/>
<sjg:gridColumn name="amount" title="Amount" width="100" sortable="false"formatter="number"/>
<sjg:gridColumn name="desc" title="Description" width="100" sortable="false"/>
</sjg:grid>
<sjg:gridColumn name="idstudent" index="idstudent" title="ID" formatter="integer" sortable="false" key="true"/>
<sjg:gridColumn name="name" index="name" title="Name" sortable="true" editable="false"/>
<sjg:gridColumn name="address" index="address" title="Address" sortable="true" editable="false"/>
<sjg:gridColumn name="email" index="email" title="Email" sortable="true" editable="false"/>
<sjg:gridColumn name="phoneno" index="phoneno" title="Phoneno" sortable="true" editable="false"/>
</sjg:grid>
</body>
</html>
Above two lines have been mentioned in bold, first one: <sj:head jqueryui="true" jquerytheme="start"/> corresponds to the use of Jquery UI
and the second one: <sjg:gridColumn name="idstudent" index="idstudent" title="ID" formatter="integer" sortable="false" key="true"/> specifies that when an element of the outer grid is expanded then the key value(idstudent) is passed as id to the subGridUrl action of the inner action, according to which data is being fetched and returned as JSON in the inner grid. If key is set to false then the usual serial number of the record being selected for expansion(clicking on plus sign) is passed as id. Therefore, in both cases the action which populates the inner grid must have an attribute named id with its corresponding getter and setter.
Hope so this will we be useful to you. Comments are always welcome in case of any kind of problem/issue in the given example.