I was trying to use TreeTable control with Odata model binding. Documentation in this area is far from perfect and searching in SDN and internet returned more questions around this topic than answers.
After analyzing source code, unit tests and some intensive debugging sessions I managed to bind TreeTable to Odata model. In this blog I’d like to describe the steps required to achieve it in two different ways: using navigation properties and OData annotations.
Expected result of this example is to have an OData service returning hierarchy data without limitation of depth and simple UI5 application to display the data in TreeTable.
1. Navigation properties
Create new OData service
Create Entity: Node
Add Properties:
Id (key) |
|
Name |
|
ParentId |
|
Create Entity Set: Nodes
Create Association: NodeToParent
Create Navigation Property: ChildNodes
All Artifacts:
Implement NODES_GET_ENTITYSET in DPC_EXT class
METHOD nodeset_get_entityset.
DATA: ls_entityLIKE LINE OF et_entityset,
ls_key LIKE LINE OF it_key_tab.
CLEAR es_response_context-inlinecount.
* no navigation path - top level call
IF lines( it_navigation_path) = 0.
DO 3 TIMES.
ls_entity-id= sy-index.
CONDENSE ls_entity-id.
ls_entity-name= |Node { ls_entity-id }|.
APPEND ls_entityTO et_entityset.
ENDDO.
es_response_context-inlinecount= lines( et_entityset).
RETURN.
ENDIF.
READ TABLE it_key_tabINTO ls_keyINDEX 1.
IF strlen(ls_key-value ) > 5. "stop here
RETURN.
ENDIF.
* this is a call using navigation from parent
* Read key of parent node and return child nodes
IF lines( it_navigation_path) > 0.
DO 5 TIMES.
ls_entity-parentid= ls_key-value.
ls_entity-id= |{ ls_entity-parentid }.{ sy-index }|.
CONDENSE ls_entity-id.
ls_entity-name= |Node { ls_entity-id }|.
APPEND ls_entityTO et_entityset.
ENDDO.
es_response_context-inlinecount= lines( et_entityset).
RETURN.
ENDIF.
ENDMETHOD.
Create UI5 application – index.html
<!DOCTYPE HTML><html><head><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta http-equiv='Content-Type' content='text/html;charset=UTF-8' /><script src="resources/sap-ui-core.js" id="sap-ui-bootstrap" data-sap-ui-libs="sap.ui.commons,sap.ui.table" data-sap-ui-theme="sap_bluecrystal"></script><script> //Create an instance of the table control var oTable = new sap.ui.table.TreeTable({ columns : [ new sap.ui.table.Column({ label : "Id", template : "Id" }), new sap.ui.table.Column({ label : "Name", template : "Name" }) ], selectionMode : sap.ui.table.SelectionMode.Single, enableColumnReordering : true, }); //Create a model and bind the table rows to this model //navigation service var sServiceUrl = "/sap/opu/odata/sap/ZT38MP_TM_HIER_SRV/"; var oModel = new sap.ui.model.odata.v2.ODataModel(sServiceUrl, { useBatch : true }); oTable.setModel(oModel); //navigation service binding oTable.bindRows({ path : "/Nodes", parameters : { expand : "ChildNodes", navigation : { 'Nodes' : 'ChildNodes' } } }); //Bring the table onto the UI oTable.placeAt("content");</script></head><body class="sapUiBody" role="application"><div id="content"></div></body></html>
Result:
2. Annotations
We will use following annotations: hierarchy-node-for, hierarchy-level-for, hierarchy-parent-node-for, hierarchy-drill-state-for
List of all SAP annotations for OData Version 2.0 http://scn.sap.com/docs/DOC-44986
Create new OData service
Create Entity: Node
Add Properties:
Id (key) |
|
Name |
|
ParentId |
|
Level |
|
DrilldownState |
|
Create Entity Set: Nodes
All Artifacts:
Go to Model class (MPC_EXT) and redefine DEFINE method to add annotations:
METHOD define.
super->define( ).
DATA:
lo_annotation TYPE REF TO /iwbep/if_mgw_odata_annotation, "#EC NEEDED
lo_entity_type TYPE REF TO /iwbep/if_mgw_odata_entity_typ, "#EC NEEDED
lo_complex_type TYPE REF TO /iwbep/if_mgw_odata_cmplx_type, "#EC NEEDED
lo_property TYPE REF TO /iwbep/if_mgw_odata_property, "#EC NEEDED
lo_entity_set TYPE REF TO /iwbep/if_mgw_odata_entity_set. "#EC NEEDED************************************************************************************************************************************ ENTITY - Node***********************************************************************************************************************************
lo_entity_type = model->get_entity_type(iv_entity_name = 'Node' ).
************************************************************************************************************************************Properties***********************************************************************************************************************************
lo_property = lo_entity_type->get_property(iv_property_name = 'Id' ).
lo_annotation = lo_property->/iwbep/if_mgw_odata_annotatabl~create_annotation(iv_annotation_namespace = /iwbep/if_mgw_med_odata_types=>gc_sap_namespace ).
lo_annotation->add(
iv_key = /iwbep/if_ana_odata_types=>gcs_ana_odata_annotation_key-hierarchy_node_for
iv_value = 'Id' ).
lo_property = lo_entity_type->get_property(iv_property_name = 'Level' ).
lo_annotation = lo_property->/iwbep/if_mgw_odata_annotatabl~create_annotation(iv_annotation_namespace = /iwbep/if_mgw_med_odata_types=>gc_sap_namespace ).
lo_annotation->add(
iv_key = /iwbep/if_ana_odata_types=>gcs_ana_odata_annotation_key-hierarchy_level_for
iv_value = 'Id' ).
lo_property = lo_entity_type->get_property(iv_property_name = 'ParentId' ).
lo_annotation = lo_property->/iwbep/if_mgw_odata_annotatabl~create_annotation(iv_annotation_namespace = /iwbep/if_mgw_med_odata_types=>gc_sap_namespace ).
lo_annotation->add(
iv_key = /iwbep/if_ana_odata_types=>gcs_ana_odata_annotation_key-hierarchy_parent_node_for
iv_value = 'Id' ).
lo_property = lo_entity_type->get_property(iv_property_name = 'DrilldownState' ).
lo_annotation = lo_property->/iwbep/if_mgw_odata_annotatabl~create_annotation(iv_annotation_namespace = /iwbep/if_mgw_med_odata_types=>gc_sap_namespace ).
lo_annotation->add(
iv_key = /iwbep/if_ana_odata_types=>gcs_ana_odata_annotation_key-hierarchy_drill_state_for
iv_value = 'Id' ).
ENDMETHOD.
Implement NODES_GET_ENTITYSET
METHOD nodeset_get_entityset.
DATA: ls_filter LIKE LINE OF it_filter_select_options,
ls_filter_propLIKE LINE OF ls_filter-select_options.
DATA: ls_entityLIKE LINE OF et_entityset,
lv LIKE sy-index.
CLEAR es_response_context-inlinecount.
READ TABLE it_filter_select_options INTO ls_filter WITH KEY property = 'ParentId'.
IF sy-subrc= 0.
READ TABLE ls_filter-select_options INTO ls_filter_prop INDEX 1.
DO 5 TIMES.
ls_entity-parentid= ls_filter_prop-low.
ls_entity-id= |{ ls_entity-parentid }.{ sy-index }|.
CONDENSE ls_entity-id.
ls_entity-name= |Node { ls_entity-id }|.
IF strlen(ls_entity-id ) > 5. "stop here
ls_entity-drilldownstate= 'leaf'.
ENDIF.
APPEND ls_entityTO et_entityset.
ENDDO.
ENDIF.
READ TABLE it_filter_select_options INTO ls_filter WITH KEY property = 'Level'.
IF sy-subrc= 0.
READ TABLE ls_filter-select_options INTO ls_filter_prop INDEX 1.
IF ls_filter_prop-low = 0.
DO 3 TIMES.
ls_entity-id= sy-index.
CONDENSE ls_entity-id.
ls_entity-name= |Node { ls_entity-id }|.
APPEND ls_entityTO et_entityset.
ENDDO.
es_response_context-inlinecount= lines( et_entityset).
RETURN.
ENDIF.
ENDIF.
ENDMETHOD.
Create UI5 application – index.html
<!DOCTYPE HTML><html><head><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta http-equiv='Content-Type' content='text/html;charset=UTF-8' /><script src="resources/sap-ui-core.js" id="sap-ui-bootstrap" data-sap-ui-libs="sap.ui.commons,sap.ui.table" data-sap-ui-theme="sap_bluecrystal"></script><script> //Create an instance of the table control var oTable = new sap.ui.table.TreeTable({ columns : [ new sap.ui.table.Column({ label : "Id", template : "Id" }), new sap.ui.table.Column({ label : "Name", template : "Name" }) ], selectionMode : sap.ui.table.SelectionMode.Single, enableColumnReordering : true, }); //Create a model and bind the table rows to this model //annotation service var sServiceUrl = "/sap/opu/odata/sap/ZT38MP_TM_HIER2_SRV_01/"; var oModel = new sap.ui.model.odata.v2.ODataModel(sServiceUrl, { useBatch : true }); oTable.setModel(oModel); //annotation service binding oTable.bindRows({ path : "/Nodes", parameters : { countMode: "Inline", numberOfExpandedLevels : 2 } }); //Bring the table onto the UI oTable.placeAt("content");</script></head><body class="sapUiBody" role="application"><div id="content"></div></body></html>
Result: