I'll start with the fact that the whole last month I've been heavily participating in some strange and interesting Web Dynpro application development. What was the interesting in that development? The first thing is the Java application was completely based on ABAP backend. The application loaded all the necessary data from the backend, user worked on the data, modified it and sent it back to R3. Nothing unusual till the moment. Right?... But, there was the serious restriction that I was not allowed to change anything in the backend logic, I could not request change in the BAPI interface or implementation. It was just a black box for me.
One of the requirements was to design a horizontal table. I mean the table which displays a data in row manner. The peculiar feature of such table is a horizontal column expansion with the column scroll bar:
I began working on the table with enthusiasm, but almost immediatelly realized that I did not have such BAPI which would return me the data exactly as I'd like to display... No, Wrong... I had a BAPI, but its output format was in usual table form, not as I required. So, since I could not obtain another BAPI I had to convert the BAPI output (see below) into a horizontal representation and display it on UI:
Year | Salary | Profit |
---|---|---|
1/30/2004 | 1,210 | 93,243 |
1/30/2005 | 1,789 | 68,041 |
1/30/2006 | 1,437 | 18,815 |
1/30/2007 | 1,861 | 48,302 |
1/30/2008 | 1,123 | 28,766 |
After a while searching throught SDN forums in forum Display table in row wise it's mentioned two solutions:
So I decided to enhance the second solution and overcome the mentioned issues.
I used standard Table control partly designed in studio and partly generated at runtime. The main idea is to bind the table columns to set of calculated attributes in order to represent the table data in 'horizontal' form. The calculated attributes are also generated at runtime. Getter/setters for the attributes are impemented as calculated attribute accessors. For the purpose WebDynpro provides special interface IWDCalculatedAttributeAccessor.
In the context below AnnualIndicators(0..N) contains the original data in 'vertical' form. This node could be in any form (value or model) and is completely determined by the data source (BAPI output, like in my case, or Web Service output, etc.). Node HorizontalTableData(0..N) is designed specially for the table representation and bound to the table control. The node contains 'the same data' as in AnnualIndicators, but inverted in the way where columns of AnnualIndicators becoming rows in HorizontalTableData:
Supply function for node HorizontalTableData is called just one time independently from AnnualIndicators. N of elements is less or equals to N of columns in AnnualIndicators:
public void supplyTableData(IPrivateHorizontalTableView.IHorizontalTableDataNode node, IPrivateHorizontalTableView.ITableDataElement parentElement) {
//@@begin supplyTableData(IWDNode,IWDNodeElement)
final String[] VERTICAL_ATTRS = { "salary", "profit" };
final String[] VERTICAL_ATTR_LABELS = { "Salary", "Profit" };
for (int i = 0; i<VERTICAL_ATTRS.length; i++) {
IHorizontalTableDataElement indicatorEl = node.createHorizontalTableDataElement();
indicatorEl.setIndicator(VERTICAL_ATTR_LABELS[i]);
node.addElement(indicatorEl);
}
//@@end
}
Calculated attribute accessors allows node HorizontalTableData to have an access to the data stored physically in node AnnualIndicators. In other words, elements of HorizontalTableData do not keep any data in attributes, almost all the attributes are calculated and read/write data from/to AnnualIndicators. In fact the accessors implement absolutely transparent data mapping between AnnualIndicators and HorizontalTableData. Please, see the source code of the acccessors at the end of the blog. The Java class is universal, not applied to any concrete tables or context nodes. It can be completely reused in other UI components.
The table on design time contains just one read-only column Indicators. The first column has fixed position on the left and plays role of a header like in a standard table:
All other table columns are generated dynamically at runtime, but the generation is performed just one time during the view construction. Each generated column displays data extracted from the corresponding element of node AnnualIndicators. However, the number of dynamical columns is pure statical constant and does not depend actually on the number of 'vertical' rows. I mean that we generate some hardcoded maximal static number of columns the table is ever able to display. At runtime we will just hide unnecessary columns by manipulating Visibility property. If there is no such 'vertical' row in AnnualIndicators the corresponding column will be invisible:
/*
* Created on 02.11.2009
*/
package com.epam.test.ui.table.row;
import com.sap.tc.webdynpro.progmodel.api.IWDAttributeInfo;
import com.sap.tc.webdynpro.progmodel.api.IWDCalculatedAttributeAccessor;
import com.sap.tc.webdynpro.progmodel.api.IWDNode;
import com.sap.tc.webdynpro.progmodel.api.IWDNodeElement;
import com.sap.tc.webdynpro.progmodel.api.WDVisibility;
/**
* Calculated attribute accessors for 'horizontal' tables.
*
* @author Siarhei_Pisarenka
*/
public abstract class HorizontalCalcAttrAccessors {
/** Abstract accessor. Just stores original 'vertical' node as data source and table column index. */
private abstract static class AbstractHorizontalAttrAccessor
implements IWDCalculatedAttributeAccessor {
final int iCol;
final IWDNode verticalNode;
public AbstractHorizontalAttrAccessor(int iCol, IWDNode verticalNode) {
this.iCol = iCol;
this.verticalNode = verticalNode;
}
};
/**
* Abstract horizontal attribute accessor. The accessor shall be setup for an attribute in the 'horizontal'
* node. It allows to get an access to the particular element of the original 'vertical' node and display
* the row data in the corresponding table column. The stored column index of the accessor is interpreted
* at runtime as index of the element if the 'vertical' node.
*
* Sub-classes of the accessor shall provide 'vertical' attribute name.
*/
private abstract static class HorizontalAttrAccessor extends AbstractHorizontalAttrAccessor {
public HorizontalAttrAccessor(int iCol, IWDNode verticalNode) {
super(iCol, verticalNode);
}
public final void set(IWDNodeElement el, IWDAttributeInfo attr, Object value) {
verticalNode.getElementAt(iCol).setAttributeValue(getVerticalAttrName(el), value);
}
public final Object get(IWDNodeElement el, IWDAttributeInfo attr) {
return verticalNode.getElementAt(iCol).getAttributeValue(getVerticalAttrName(el));
}
protected abstract String getVerticalAttrName(IWDNodeElement hEl);
};
/**
* Horizontal attribute accessor for table columns. The accessor additionally keeps 'vertical' node
* attribute name. At runtime the accessor redirects any calls to the 'horizontal' table column attribute
* to the corresponding element of the original 'vertical' node keeping in mind the 'vertical' attribute.
*
* The accessor allows to bind column headers, column visibilities and other attributes.
*/
public static class HorizontalHeaderAttrAccessor extends HorizontalAttrAccessor {
final String verticalAttr;
public HorizontalHeaderAttrAccessor(int iCol, IWDNode verticalNode, String verticalAttr) {
super(iCol, verticalNode);
this.verticalAttr = verticalAttr;
}
protected String getVerticalAttrName(IWDNodeElement hEl) {
return verticalAttr;
}
};
/**
* Horizontal attribute accessor for table cells. The accessor extends HorizontalHeaderAttrAccessor and
* additionally keeps the whole list of 'vertical' node attribute names. Each 'horizontal' row
* corresponds to the attribute name in the list by row index. At runtime the mapping allows to redirect
* each 'horizontal' row coming to the accessor to the corresponding 'vertical' attribute.
*
* The accessor allows to bind cell editors.
*/
public static class HorizontalCellAttrAccessor extends HorizontalAttrAccessor {
final String[] verticalAttrs;
public HorizontalCellAttrAccessor(int iCol, IWDNode verticalNode, String[] verticalAttrs) {
super(iCol, verticalNode);
this.verticalAttrs = verticalAttrs;
}
protected String getVerticalAttrName(IWDNodeElement hEl) {
return verticalAttrs[hEl.index()];
}
};
/**
* Horizontal attribute getter for table column visibilities. Allows to hide unnecessary table columns
* that are behind the original 'vertical' node rows. This means that the column is visible only if such row
* exists in the original 'vertical' node.
*/
public static class HorizontalColumnVisibilityAttrAccessor
extends AbstractHorizontalAttrAccessor {
public HorizontalColumnVisibilityAttrAccessor(int iCol, IWDNode verticalNode) {
super(iCol, verticalNode);
}
public Object get(IWDNodeElement el, IWDAttributeInfo attr) {
return (iCol < verticalNode.size()) ? WDVisibility.VISIBLE : WDVisibility.NONE;
}
public void set(IWDNodeElement arg0, IWDAttributeInfo arg1, Object arg2) {
throw new UnsupportedOperationException();
}
};
}
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
8 | |
5 | |
5 | |
4 | |
4 | |
4 | |
3 | |
3 | |
3 | |
3 |