Often you must influence the rendering of the HTMLB tableView. Maybe more (virtual) columns are required, or the presentation of the data should not be done as text, but as icons. For this, the HTMLB tableView supports the concept of an iterator. The tableView will use this callback interface during rendering for each row and cell.
This article will look in detail at the HTML tableView iterator, and show it in action. An absolute prerequisite is the first article: “BSP Programming: BSP Element Expressions (BEEs)”.
In the first step, we write a small BSP that will display a table. The complete source is:
Type Definitions:
TYPES: TABLE_SFLIGHT TYPE TABLE OF SFLIGHT.
Page Attributes:
flights TYPE TABLE_SFLIGHT
iterator TYPE REF TO IF_HTMLB_TABLEVIEW_ITERATOR
OnCreate Event:
SELECT * FROM SFLIGHT INTO TABLE flights.
Layout: <%@page language="abap"%>
<%@extension name="htmlb" prefix="htmlb"%>
<htmlb:content design="design2002">
<htmlb:page>
<htmlb:form>
<htmlb:tableView id = "tv1"
visibleRowCount = "10"
selectionMode = "lineEdit"
table = "<%=flights%>"
iterator = "<%=iterator%>" />
</htmlb:form>
</htmlb:page>
</htmlb:content>
For this test, we will display some information from the SFLIGHT table. We define a “type table of sflight”, and then declare a page attribute of this table type to hold all records from the database. In the layout section, the HTMLB tableView is used to display the table.
The output is as expected, not very exiting:
For this article, we would like to do some rendering “improvements”:
In principle, an HTMLB iterator is any class that implements the interface IF_HTMLB_TABLEVIEW_ITERATOR.
interface IF_HTMLB_TABLEVIEW_ITERATOR public.
methods GET_COLUMN_DEFINITIONS ...
methods RENDER_ROW_START ...
methods RENDER_CELL_START ...
endinterface.
This interface supports three methods. The column definitions method is called once at the beginning of rendering, to allow an update or complete specification of all column definitions. The row and cell start methods are called at the start of a new row and cell respectively. The complete parameter list will be discussed later.
Let’s complete the last part of our test program. We need an iterator to enable custom rendering of the HTMLB tableView. For this, we use transaction SE24 (or SE80), and create a new class that implements the iterator interface. All methods are implemented initially as empty.
Our iterator class looks like this:
class CL_BCM_SDN_ITERATOR definition public create public.
public section.
interfaces IF_HTMLB_TABLEVIEW_ITERATOR .
endclass.
class CL_BCM_SDN_ITERATOR implementation.
method IF_HTMLB_TABLEVIEW_ITERATOR~GET_COLUMN_DEFINITIONS.
endmethod.
method IF_HTMLB_TABLEVIEW_ITERATOR~RENDER_CELL_START.
endmethod.
method IF_HTMLB_TABLEVIEW_ITERATOR~RENDER_ROW_START.
endmethod.
endclass.
As a final step, we extend the onCreate event to also instantiate such an iterator:
OnCreate Event:
SELECT * FROM SFLIGHT INTO TABLE flights.
CREATE OBJECT iterator TYPE CL_BCM_SDN_ITERATOR.
Important: It is unfortunately not possible to implement local classes inside a BSP page. So we must implement the iterator interface somewhere else. For this article, a new class is created to implement the interface. If the HTMLB tableView is used in a view, another idea is to implement the iterator interface in the calling controller. Alternatively, place the iterator inside the application class. However, this approach becomes difficult when more than one iterator is required. The recommendation is to use local classes in such cases.
The method GET_COLUMN_DEFINITIONS is called at the beginning of the rendering. It receives the list of existing column definitions, and can update the list. This method has three parameters:
Parameter | Comments |
P_TABLEVIEW_ID Importing STRING | The ID of the current table being rendered is supplied, for the situation that the same iterator implementation is used for more than one table. |
P_COLUMN_DEFINITIONS Changing TABLEVIEWCONTROLTAB | The columns definition is actually the most interesting parameter. It contains the content of all HTMLB tableViewColumn BSP elements. (For a detailed description of all fields, please refer to the documentation of this BSP element and/or see DDIC definition for this structure.) |
P_OVERWRITES Changing TABLEVIEWOVERWRITETAB | This allows the iterator to fill a table of special BEEs that will be rendered at the specific row and column indexes. However, the overwritten BEEs must all be created in advance, without even knowing if they will be used. Our recommendation is to ignore this attribute. (We consider it a dud attribute that should never have been defined!) |
Usually, column definitions are done with inner tags (HTMLB tableViewColumn). This is still possible, and the already configured columns will be listed in the column definition table. However, setting the column definitions dynamically cleans up the layout, and allows the flexibility to decide at runtime which columns should be rendered.
For our test table, the complete coding is:
method IF_HTMLB_TABLEVIEW_ITERATOR~GET_COLUMN_DEFINITIONS.
FIELD-SYMBOLS: <def> LIKE LINE OF p_column_definitions.
* First column is small icon.
APPEND INITIAL LINE TO p_column_definitions ASSIGNING <def>.
<def>-COLUMNNAME = 'ICON'.
<def>-TITLE = ' '.
* Next display standard columns from table: CARRID, CONNID, FLDATE,
* PRICE, CURRENCY & PLANETYPE. EDIT flag is used in LINEEDIT mode
* to determine which columns can be edited.
APPEND INITIAL LINE TO p_column_definitions ASSIGNING <def>.
<def>-COLUMNNAME = 'CARRID'.
APPEND INITIAL LINE TO p_column_definitions ASSIGNING <def>.
<def>-COLUMNNAME = 'CONNID'.
APPEND INITIAL LINE TO p_column_definitions ASSIGNING <def>.
<def>-COLUMNNAME = 'FLDATE'. <def>-EDIT = 'X'.
APPEND INITIAL LINE TO p_column_definitions ASSIGNING <def>.
<def>-COLUMNNAME = 'PRICE'. <def>-EDIT = 'X'.
APPEND INITIAL LINE TO p_column_definitions ASSIGNING <def>.
<def>-COLUMNNAME = 'CURRENCY'. <def>-EDIT = 'X'.
APPEND INITIAL LINE TO p_column_definitions ASSIGNING <def>.
<def>-COLUMNNAME = 'PLANETYPE'.
* Finally we add new column for seats
APPEND INITIAL LINE TO p_column_definitions ASSIGNING <def>.
<def>-COLUMNNAME = 'SEATS'.
<def>-TITLE = 'Seats'(001).
<def>-EDIT = 'X'.
endmethod.
The column definition structure provides us with many options for fine-tuning the display of each column. However, for this example, only a few options will be sufficient.
As our first step, we add a new column called ‘ICON’. As this column does not exist in the table, it cannot be rendered by the HTMLB tableView.
In the next block only those columns that must be displayed are listed. The names used match column names defined in the table. For some columns, we set the EDIT flag to indicate that these columns are editable. By default, no columns are editable. Also, no title information is set. This will be read directly from DDIC.
Last, we add our new SEATS column that will be the sum of all values.
The new output shows a dramatic improvement:
Note that the NULL values reflect the fact that the tableView has no values for these virtual columns. These will be set later.
The method RENDER_ROW_START is called once at the beginning of each row. The biggest benefit from this call is to dynamically load relevant data for only those rows that will be rendered. (Important: this method is only called for rows actually rendered. By delaying work to this point, it means that less work may be done for all rows.)
This method has a number of parameters, of which the most interesting is the data reference to the actual row. Because the HTMLB tableView works generically with tables, it cannot supply a typed reference. However, the iterator (usually!) knows the type, and should cast this reference into the correct type.It is the fastest way to access the current row data.
The complete list of parameters is:
Parameter | Comments |
P_TABLEVIEW_ID Importing STRING | The ID of the current table being rendered is supplied, for the situation that the same iterator implementation is used for more than one table. |
P_ROW_INDEX Importing I | The table index of the row that will be rendered. |
P_ROW_KEY Importing STRING | If a key column has been defined, the key for the row will be supplied. |
P_ROW_DATA_REF Importing Ref To DATA | This is a reference to the current row to be rendered. Probably the most important parameter. It is not necessary to reload the data! |
P_EDIT_MODE Importing XFELD | Indicator whether this row is in edit mode. |
P_SKIP_ROW Returning XFELD | Flag that can be set to indicate that this row should not be rendered. It can be used to implement user-defined filters. |
For our test program, we only store the reference to the actual data row. For this, we define a new class attribute “m_row_ref TYPE REF TO SFLIGHT” in the iterator class. The coding is short andbrutal:
method IF_HTMLB_TABLEVIEW_ITERATOR~RENDER_ROW_START.
m_row_ref ?= p_row_data_ref.
endmethod.
That little question mark is not our uncertainty, but a cast operator in ABAP :).
The RENDER_CELL_START method will be called for each and every cell that will be rendered, also for cells in virtual columns. In all cases, we highly recommend to do nothing to enable the default rendering of the HTMLB tableView, unless custom rendering is required.
This method supports a large number of parameters. The most interesting ones are listed below. Note that many parameters are equal to those of RENDER_ROW_START. Of course, we recommend to do work once for the entire row. But programmers are lazy, and as such the parameters are made available for each cell call. The parameters that are the same as RENDER_ROW_START are not listed again.
Parameter | Comments |
P_CELL_ID Importing STRING | The correct (HTML) ID that has been computed for this cell. This value contains the tableView ID, the row and column index. |
P_CELL_BINDING Importing STRING | This value is only set if the table has been bound. This is the binding path for the cell being rendered. It can be used in other BSP elements. |
P_COLUMN_INDEX Importing I | Index of current column (relative to column definitions). |
P_COLUMN_KEY Importing STRING | Name of the column being rendered. |
P_REPLACEMENT_BEE Exporting Ref To IF_BSP_BEE | If this value is left initial, the default HTMLB tableView rendering action will be taken. However, with this exporting parameter, it is possible to set a new BEE that will then be rendered into the current cell. This new BEE can (but does not have to) keep the current EDIT mode in mind. |
The best approach to RENDER_CELL_START is to always implement only the absolute minimum. Leave the heavy lifting to the HTMLB tableView. For our example, we wish to custom render the ICON and SEAT fields. For the FLDATE and CURRENCY fields, we want to accept the default display handling, and use a different rendering only for editing.
The skeleton code is:
method IF_HTMLB_TABLEVIEW_ITERATOR~RENDER_CELL_START.
CASE p_column_key.
WHEN 'ICON'.
WHEN 'FLDATE'.
IF p_edit_mode IS NOT INITIAL.
ENDIF.
WHEN 'CURRENCY'.
IF p_edit_mode IS NOT INITIAL.
ENDIF.
WHEN 'SEATS'.
ENDCASE.
endmethod.
In the next step we wish to complete the code for these four columns. In all cases, when we require direct access to the data from the row that is currently been rendered, we use the row reference stored (which has the correct data type). This is fast, clean and safe. Specifically, no dynamic programming is done with this type of access, and the compiler can completely check at compile time that we are referencing the correct data in the correct format.
WHEN 'ICON'.
DATA: icon_plane TYPE STRING.
icon_plane = CL_BSP_MIMES=>SAP_ICON( id = 'ICON_WS_PLANE' ).
p_replacement_bee = CL_HTMLB_IMAGE=>FACTORY( id = p_cell_id src = icon_plane ).
For the ICON column, we require a small icon at all times. (We like icons, even if they are not acceptable for our usability engineers ;). So an HTMLB image is created and returned as BEE.
For the flight date, we are only interested in updating the edit case. Usually a normal input field is rendered by the HTMLB tableView. However, we know that this value is of type date and we require a little date picker as part of the input field. The only other problem in the code is that the value of FLDATE cannot be directly passed as STRING parameter. A string conversion is required first, and this adds the additional two lines of code!
WHEN 'FLDATE'.
IF p_edit_mode IS NOT INITIAL.
DATA: date TYPE STRING.
date = m_row_ref->FLDATE.
p_replacement_bee = CL_HTMLB_INPUTFIELD=>FACTORY(
id = p_cell_id
value = date
type = 'DATE'
showHelp = 'TRUE'
cellValue = 'TRUE' ).
ENDIF.
The cellValue parameter indicates to the HTMLB inputField that it is rendering in the context of an HTMLB tableView cell, and should not add its own frame around the input field.
For currency, we are again only interested in handling the Edit mode. For this, we want to render a dropdown listbox. There are a number of techniques to fill the data. For this test, we created a name/value table (type TIHTTPNVP) in the class constructor, and already filled it with the currencies that we support. A reference to this table is stored in m_currencies_ref (ABAP statement “GET REFERENCE OF var INTO ref”).
WHEN 'CURRENCY'.
IF p_edit_mode IS NOT INITIAL.
p_replacement_bee = CL_HTMLB_DROPDOWNLISTBOX=>FACTORY(
id = p_cell_id
selection = m_row_ref->CURRENCY
table = me->m_currencies_ref
nameOfKeyColumn = 'NAME'
nameOfValueColumn = 'VALUE' ).
ENDIF.
For the SEATS column, our work is slightly more complex. So first the code:
WHEN 'SEATS'.
IF p_edit_mode IS INITIAL.
DATA: max TYPE STRING, occ TYPE STRING, value TYPE STRING.
max = m_row_ref->SEATSMAX + m_row_ref->SEATSMAX_B + m_row_ref->SEATSMAX_F.
occ = m_row_ref->SEATSOCC + m_row_ref->SEATSOCC_B + m_row_ref->SEATSOCC_F.
CONDENSE: max, occ.
CONCATENATE occ ` / ` max INTO value.
p_replacement_bee = CL_HTMLB_TEXTVIEW=>FACTORY( text = value ).
ELSE.
DATA: if_first TYPE REF TO CL_HTMLB_INPUTFIELD.
if_first = CL_HTMLB_INPUTFIELD=>FACTORY( id = p_cell_id id_postfix = '_first'
type = 'INTEGER' size = '4' ).
if_first->VALUE = m_row_ref->SEATSOCC_F.
DATA: if_bus TYPE REF TO CL_HTMLB_INPUTFIELD.
if_bus = CL_HTMLB_INPUTFIELD=>FACTORY( id = p_cell_id id_postfix = '_bus'
type = 'INTEGER' size = '4' ).
if_bus->VALUE = m_row_ref->SEATSOCC_B.
DATA: if_econ TYPE REF TO CL_HTMLB_INPUTFIELD.
if_econ = CL_HTMLB_INPUTFIELD=>FACTORY( id = p_cell_id id_postfix = '_econ'
type = 'INTEGER' size = '4' ).
if_econ->VALUE = m_row_ref->SEATSOCC.
DATA: seats_bee TYPE REF TO CL_BSP_BEE_TABLE.
CREATE OBJECT seats_bee.
seats_bee->ADD( if_first ).
seats_bee->ADD( if_bus ).
seats_bee->ADD( if_econ ).
p_replacement_bee = seats_bee.
ENDIF.
For the display part, we need to show only the final totals of the form “occupied / max”. The work reduces to calculating the values for the current row that has been rendered, and to place them into a string. An HTMLB textView is used to render the string. (Important: this display was not selected to be functionally perfect, but more to have a little complexity to show data manipulation directly against the selected table row. As such, the achieved layout is not recommended for any development!)
The code for edit mode is only slightly more complex. For this test, we’ll show all three values directly inline, each in its own input field. The complete code consists of creating three HTMLB inputFields, and using a table BEE to build them into one expression. All BEE handling has already been extensively discussed in the prerequisite article.
In the code above, there are three interesting aspects. The first is that the FACTORY parameter id_postfix is used to create new IDs for each input field. The postfix string is appended onto the supplied ID in the factory method. The other interesting aspect is that the value is not set during the factory call. It is not possible to supply INT4 values to STRING parameters. The values are set directly after the factory call to use ABAP MOVE conversion semantics. This way of initializing a BSP element is completely acceptable. Finally, we decided not to set the cellValue attribute for each input field, as we wanted the additional frame around each field. (No, this is not a perfect display, but one picked to teach!)
The final output is perfect – just what we expected:
Input handling of edited cells has not been discussed. This is left as a topic for another article. (First we must ask Steffen, the real expert behind the tableView, for the answers.)
Nearly all the work for rendering cells, in both display and input mode, is done by the HTMLB tableView. We only had to add about 70 lines of code to get the special cases rendered correctly. Using BEEs and the HTMLB iterator together has pulled us out of many a tight spot. This can be considered a critical part of any BSP programmer’s toolbox.