BSP Programming: HTMLB TableView Iterator
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”:
- Render a new column in the beginning with a little plane icon.
- Instead of having size columns for first, business and economy class seats available and occupied, we would like to render one column of the format “Occupied/Max”.
- In edit mode, the currency should be picked from a dropdown listbox, and it should be possible to edit the occupation for the different seats individually (even if they are displayed only in summed format!).
What Is an HTMLB Iterator?
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:
|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:
|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.
|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 Look
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.
Is it possible to manipulate the selectionmode? I want to hide some checkboxes on my Sites.
Forgive me if I've totally lost the plot but I'm still battling to get a Tableview to display certian cells in different colors (red etc)...
I've implemented the IF_HTMLB_TABLEVIEW_ITERATOR interface and have written code for the "render_cell_start" method....it has something like the following...
p_style = 'celldesign:stcd_positive'.
BUT nothing happens...I've tried to look up some example code and have been to the WebLogs etc and still can't get it right.
It's probably something small I'm doing wrong...please help!!!
you should try p_style = 'cellDesign:NEGATIVE' (only for DESIGN 2003!). Then it should work.
I found this weblog really interessting and I would appreciate to read the article about handling the edit mode of the created table, especially the fields with first, business and economy class seats. I hope, this article will be posted soon.
The Dropdownlist of the column 'CURRENCY' is empty, allthough me->m_currencies_ref is filled?
Many Thanks for your help
how to show 'f4 help' for inputField type date
by using Iterator. This article was great help for me. Thank you.
The following code is what I wrote in the
method 'IF_HTMLB_TABLEVIEW_ITERATOR~RENDER_CELL_START' of my Iterator class.
P_REPLACEMENT_BEE = CL_HTMLB_INPUTFIELD=>FACTORY(
id = TMP_ID
value = TMP_DATUM
type = 'DATE'
showHelp = 'TRUE'
cellValue = 'TRUE'
maxlength = '10'
width = '80%'
ALIGNMENT = 'CENTER'
We are running WAS 620 and i tried to use your example. When i check the syntax it says,
" The type of the database table and work area(or internal table) "FLIGHTS" are not unicode convertible"
What is the problem in this?
SDN is very much a community for developers. However, there is a fine line between questions that the document should answer, and those more "tough" questions (the "how do I do X"), for SDN.
For this question, I would recommend, at least quickly, reading through the documentation and reviewing the source again. My quick search gave at least this link:
If this does not help you, then open a thread on the forum with problem, relevant source code, documentation that you have checked, and we see if we can crack it.
I Need Request to Client Patch for CSV File, and upload data in ZTABLE.
Before need Delete data table.
I Use only one Field in Table : ZCLIENTE
Any idea, what is the best solution ?
I try to use tableview iterator with this article. But I met a problem with syntex error.
I made a class "Z3_CL_VIS_ITERATOR" which has code belowe.
METHOD if_htmlb_tableview_iterator~render_cell_start .
DATA : str TYPE string.
str = p_row_data_ref->vgubun.
But parser said "You can not dereference (->) a generic reference in the current statement".
Why I can use "->" operator??? Did I make class wrong?
I can edit the cells but this is not enough tu update my internal table...i dont know what is the next step to it doing from a tableview..
Please help me out.
First of all...i am comparitively new to BSP..and
I am trying to implememnt the type of code given by you...
I have created a attribute in my iterator class "m_row_ref" type ref to "Z-table"
But in my BSP application i am using a structure from the Ztable...i.e only a few fields needs to be displayed in the tableView...
Now when i execute this code...it gives...TYPE CAST ERROR...i understand why....but how do i solve this...??
Sorry if this is a silly question...!
Thanks in advance..!
I have implemented the interface and implemented method RENDER_ROW_START like this :
ROW_REF ?= p_row_data_ref.
But the p_row_data_ref is always empty. Please give me some suggestions to solve this problem.
while trying the code in this blog i am facing an issue, every time i select a new row , the value for DDLB in currency column initialises.Checked in debuging , this is because m_row_ref->currency is always NULL . i am filling the table for currencies properly in constructor the DDLB has all three values , but the problem is initialization of DDLB on any server side event.
first of all I'd like to congratule the author, very well explained and useful article.
My doubt comes when you try to do this in a view instead of a page with flow logic. In this case you don't have events where instantiate the iterator, so as it's told, we include the interface in the calling controller class. Until here there are no problems, because once you add the iterator the three methods appear and you can implement it.
But, what's the best place to put the object instantiation (create object iterator... line). For me I've created the iterator attribute in the attributes tab for the view and the in the layout tab I've put a line like this:
For those facing the issue of the BLANK drop down list for the CURRENCY field kindly do the understated additions in the custom iterator class.
Add the following two Private attributes:
1. I_CURRENCIES TYPE TIHTTPNVP
2. M_CURRENCIES_REF TYPE REF TO TIHTTPNVP
In the Constructor method fill the currency as you may need in the internal table i_currencies.
I am showing all currencies from table TCURT in the sample code below.
data wa_curr_dropdown type IHTTPNVP.
select waers ltext
where spras = sy-langu.
append wa_curr_dropdown to i_currencies_ref.
get REFERENCE OF i_currencies_ref into m_currencies_ref.
Brian, Thanks for the insight into TableView iterators.
I'm doing a development similar to that shown in this blog, I show a table and I am placing a inputField edit fields but no data are traveling, follow the steps samples, but I don't see that data travel, unlike the page updates and return to their original state.
If you had a serious tips to help perfect.