I have a grid provided by the struts2-jquery-grid-3.7.0 plugin as follows.
<s:url id="remoteurl" action="ProductGrid" namespace="/admin_side"/>
<s:url id="editurl" action="ProductCRUD"/>
<sjg:grid
id="gridmultitable"
caption="Product"
dataType="json"
href="%{remoteurl}"
pager="true"
navigator="true"
navigatorSearchOptions="{sopt:['eq','ne','lt','gt']}"
navigatorEdit="false"
navigatorView="false"
navigatorAddOptions="{height:280, width:500, reloadAfterSubmit:true}"
navigatorEditOptions="{height:280, width:500, reloadAfterSubmit:false}"
navigatorViewOptions="{height:280, width:500}"
navigatorDelete="true"
navigatorDeleteOptions="{height:280, width:500,reloadAfterSubmit:true}"
gridModel="gridModel"
rowList="5,10,15"
rowNum="5"
rownumbers="true"
editurl="%{editurl}"
editinline="true"
multiselect="true"
onSelectRowTopics="rowselect"
onEditInlineSuccessTopics="oneditsuccess"
viewrecords="true"
shrinkToFit="false"
width="1045"
>
<sjg:gridColumn name="prodId" index="prodId" title="Id" key="true" frozen="true" width="200" formatter="integer" editable="false" dataType="Long" sortable="true" search="true" sorttype="integer" searchoptions="{sopt:['eq','ne','lt','gt']}"/>
<sjg:gridColumn name="prodName" index="prodName" title="Product Name" width="200" editable="true" sortable="true" search="true" sorttype="text"/>
<sjg:gridColumn name="prodCode" index="prodCode" title="Product Code" width="200" sortable="true" search="true" editable="true" sorttype="text"/>
<!--Start nested properties-->
<sjg:gridColumn name="subCategory.category.catName" index="subCategory.category.catName" title="Category" width="200" sortable="true" search="true" editable="true" sorttype="text"/>
<sjg:gridColumn name="subCategory.subCatName" index="subCategory.subCatName" title="SubCategory" width="200" sortable="true" search="true" editable="true" sorttype="text"/>
<sjg:gridColumn name="brand.brandName" index="brand.brandName" title="Brand" width="200" sortable="true" search="true" editable="true" sorttype="text"/>
<sjg:gridColumn name="fabric.fabricName" index="fabric.fabricName" title="Fabric" width="200" sortable="true" search="true" editable="true" sorttype="text"/>
<!--End nested properties-->
<sjg:gridColumn name="marketPrice" index="marketPrice" title="Market Price" width="200" sortable="true" search="true" editable="true" sorttype="text"/>
<sjg:gridColumn name="salePrice" index="salePrice" title="Sale Price" width="200" sortable="true" search="true" editable="true" sorttype="text"/>
<sjg:gridColumn name="featured" index="featured" title="Featured" width="200" sortable="true" search="true" editable="true" sorttype="text"/>
<sjg:gridColumn name="expressDelivery" index="expressDelivery" title="Express Delivery" width="200" sortable="true" search="true" editable="true" sorttype="text"/>
<sjg:gridColumn name="weight" index="weight" title="Weight" width="200" sortable="true" search="true" editable="true" sorttype="text"/>
<sjg:gridColumn name="quantity" index="quantity" title="Quantity" width="200" sortable="true" search="true" editable="true" sorttype="text"/>
<sjg:gridColumn name="visible" index="visible" title="Visible" width="200" sortable="true" search="true" editable="true" sorttype="text"/>
<sjg:gridColumn name="latest" index="latest" title="Latest" width="200" sortable="true" search="true" editable="true" sorttype="text"/>
<sjg:gridColumn name="prodDesc" index="prodDesc" title="Description" width="200" sortable="true" search="true" editable="true" sorttype="text"/>
</sjg:grid>
As can be seen, there are some nested properties in a few columns. They are not listed (displayed) in the given grid. The associated columns are simply left blank. The rest of the fields are displayed as usual.
I have also tried enclosing them within %{...} but to no avail.
How to display such nested properties in a grid? Is there any special treatment for them?
It was verified that these nested properties were fetched from the database and the model was initialized correctly.
Edit:
The action class:
@Namespace("/admin_side")
@ResultPath("/WEB-INF/content")
@ParentPackage(value="json-default")
@InterceptorRefs({@InterceptorRef(value="store", params={"operationMode", "AUTOMATIC"})})
public final class ProductAction extends ActionSupport implements Serializable, ModelDriven<Product>
{
@Autowired
private final transient ProductService productService=null;
private static final long serialVersionUID = 1L;
private Product entity=new Product();
private List<Product>gridModel=new ArrayList<Product>();
private String id;
// Get how many rows we want to have into the grid - rowNum attribute in the grid
private Integer rows=5;
// Get the requested page. By default grid sets this to 1.
private Long page=1L;
// sorting order - asc or desc
private String sord;
// get index row - i.e. user click to sord.
private String sidx;
// Search Field
private String searchField;
// The Search String
private String searchString;
// The Search Operation ['eq','ne','lt','le','gt','ge','bw','bn','in','ni','ew','en','cn','nc']
private String searchOper;
// Your Total Pages
private Long total;
// All Records
private Long records;
private String oper;
@Override
public Product getModel() {
return entity;
}
@Action(value = "ProductCRUD",
results = {
@Result(name = ActionSupport.SUCCESS, location = "Product.jsp"),
@Result(name = ActionSupport.INPUT, location = "Product.jsp")},
interceptorRefs = {
@InterceptorRef(value = "defaultStack", params = {"validation.validateAnnotatedMethodOnly", "true", "validation.excludeMethods", "load"})})
public String edit() throws Exception {
if(oper.equalsIgnoreCase("add")) {
// Add a row.
}
else if(oper.equalsIgnoreCase("edit")) {
// Update a row.
}
else if(oper.equalsIgnoreCase("del")) {
// Delete a row.
}
return ActionSupport.SUCCESS;
}
@Action(value = "ProductGrid",
results = {
@Result(name = ActionSupport.SUCCESS, type = "json", params = {"includeProperties", "gridModel\\[\\d+\\]\\.prodId, gridModel\\[\\d+\\]\\.prodName, gridModel\\[\\d+\\]\\.prodCode, gridModel\\[\\d+\\]\\.prodDesc, gridModel\\[\\d+\\]\\.marketPrice, gridModel\\[\\d+\\]\\.salePrice, gridModel\\[\\d+\\]\\.featured, gridModel\\[\\d+\\]\\.expressDelivery, gridModel\\[\\d+\\]\\.weight, gridModel\\[\\d+\\]\\.occassion, gridModel\\[\\d+\\]\\.quantity, gridModel\\[\\d+\\]\\.visible, gridModel\\[\\d+\\]\\.latest, gridModel\\[\\d+\\]\\.subCategory, gridModel\\[\\d+\\]\\.fabric, gridModel\\[\\d+\\]\\.brand, gridModel\\[\\d+\\]\\.subCategory\\[\\d+\\]\\.category, total, records, rows, page, sord, sidx, searchField, searchString, searchOper", "excludeNullProperties", "true"})},
interceptorRefs = {
@InterceptorRef("params")})
public String executeAction() {
records=productService.rowCount().longValue();
total=new BigDecimal(records).divide(new BigDecimal(rows), 0, BigDecimal.ROUND_CEILING).longValue();
gridModel=productService.getList((int)(page-1)*rows, rows, new HashMap<String, String>(){{put(sidx, sord);}}, null);
return SUCCESS;
}
public String getJSON() {
return executeAction();
}
public List<Product> getGridModel() {
return gridModel;
}
public void setGridModel(List<Product> gridModel) {
this.gridModel = gridModel;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Integer getRows() {
return rows;
}
public void setRows(Integer rows) {
this.rows = rows;
}
public Long getPage() {
return page;
}
public void setPage(Long page) {
this.page = page;
}
public String getSord() {
return sord;
}
public void setSord(String sord) {
this.sord = sord;
}
public String getSidx() {
return sidx;
}
public void setSidx(String sidx) {
this.sidx = sidx;
}
public String getSearchField() {
return searchField;
}
public void setSearchField(String searchField) {
this.searchField = searchField;
}
public String getSearchString() {
return searchString;
}
public void setSearchString(String searchString) {
this.searchString = searchString;
}
public String getSearchOper() {
return searchOper;
}
public void setSearchOper(String searchOper) {
this.searchOper = searchOper;
}
public Long getTotal() {
return total;
}
public void setTotal(Long total) {
this.total = total;
}
public Long getRecords() {
return records;
}
public void setRecords(Long records) {
this.records = records;
}
public String getOper() {
return oper;
}
public void setOper(String oper) {
this.oper = oper;
}
@Action(value = "Product",
results = {
@Result(name=ActionSupport.SUCCESS, location="Product.jsp"),
@Result(name = ActionSupport.INPUT, location = "Product.jsp")},
interceptorRefs={
@InterceptorRef(value = "defaultStack", params = {"validation.validateAnnotatedMethodOnly", "true", "validation.excludeMethods", "load"})})
public String load() throws Exception {
// This method is only needed to return an initial view on page load. Nothing to see here. Leave it empty.
return ActionSupport.SUCCESS;
}
}
In response, those all nested properties are empty. The JSON response for a single row is as follows.
{
"gridModel": [
{
"brand": {
},
"expressDelivery": false,
"fabric": {
},
"featured": true,
"latest": false,
"marketPrice": 12.00,
"occassion": "222",
"prodCode": "aaa",
"prodDesc": "xxx",
"prodId": 5,
"prodName": "ddd",
"quantity": 1,
"salePrice": 12.00,
"subCategory": {
},
"visible": true,
"weight": 22.00
}
],
"page": 1,
"records": 5,
"rows": 5,
"sidx": "",
"sord": "asc",
"total": 1
}
subCategory.category.catNameexists in your model I mean in which form i.e bean object are you want them from Value Stack ?List<Product> gridModelwhich is populated from the database, when a request to the actionProductGridis made as shown in the grid. EachProductin the list contains asubCategoryobject which in turn, contains acategoryobject. Dereferencing all of them with adot,catNamepossessed bycategoryshould be accessed.dotand sub category ?subCategory": {}. Hence the expression likesubCategory.category.catNameis not evaluated.gridModel\\[\\d+\\]\\.subCategory\\.category\\.catName(orgridModel\\[\\d+\\]\\.subCategory\\[\\d+\\]\\.category\\.catNameif you have several subcategories for each row)