Skip to Content
Technical Articles

SAPUI5 Smart features controlled by OData Annotations

Hi,

In this post I want to give an insight into metadata based UI development. I will showcase some of the Smart component features controlled by metadata annotations. With the use of annotations and Smart components like SmartTable you can minimize the UI View code to be written for conventional scenarios like listing and filtering data.

Example1: Listing EntitySet with SmartTable

Given the following model with Products EntitySet: (from OData v2 Reference Service)

<Schema Namespace="ODataDemoService" xmlns="http://schemas.microsoft.com/ado/2007/05/edm"
            xmlns:sap="http://www.sap.com/Protocols/SAPData">
    <EntityType Name="Product">
        <Key>
            <PropertyRef Name="ID"/>
        </Key>
        <Property Name="ID" Type="Edm.Int32" Nullable="false" sap:label="ID"/>
        <Property Name="CategoryID" Type="Edm.Int32" Nullable="false" sap:label="Category"/>
        <Property Name="SupplierID" Type="Edm.Int32" Nullable="false" sap:label="Supplier"/>
        <Property Name="Name" Type="Edm.String" Nullable="true" sap:label="Name"/>
        <NavigationProperty Name="Supplier" Relationship="ODataDemo.Product_Supplier_Supplier_Products" FromRole="Product_Supplier"
            ToRole="Supplier_Products"/>		
    </EntityType>
	<EntityContainer Name="DemoService" m:IsDefaultEntityContainer="true">
		<EntitySet Name="Products" EntityType="ODataDemoService.Product" />
		</EntitySet>
	</EntityContainer>
</Schema>

and the following UI View with a SmartFilter and SmartTable component listing the Products entitySet:

<smartFilterBar:SmartFilterBar id="smartFilterBar" entitySet="Products" 
    useToolbar="false" showFilterConfiguration="false">
</smartFilterBar:SmartFilterBar>
<smartTable:SmartTable id="ProductsSmartTable" entitySet="Products" 
    smartFilterId="smartFilterBar" header="Products" showRowCount="true"
    useExportToExcel="false" useVariantManagement="false" 
    useTablePersonalisation="false">
</smartTable:SmartTable>

Running the application and pressing ‘Go’ in the smartFilterBar will give an error message:

It’s because no descriptor exists that describes how Products should be listed.

Resolution:

To select columns for smartTable, use LineItem annotation. It applies to EntityTypes, and it is a collection of data fields for representation in a table or list: (for documentations see Appendix1: Annotations, on how to add annotations see Appendix2: Adding annotations to metadata)

<Annotations Target="ODataDemoService.Product">
    <Annotation Term="com.sap.vocabularies.UI.v1.LineItem">
        <Collection>
            <Record Type="com.sap.vocabularies.UI.v1.DataField">
                <PropertyValue Property="Value" Path="Name"/>
            </Record>
            <Record Type="com.sap.vocabularies.UI.v1.DataField">
                <PropertyValue Property="Value" Path="CategoryID"/>
            </Record>
            <Record Type="com.sap.vocabularies.UI.v1.DataField">
                <PropertyValue Property="Label" String="Supplier Name"/>
                <PropertyValue Property="Value" Path="Supplier/Name"/>
            </Record>
        </Collection>
    </Annotation>
</Annotations>

Data fields are value paths pointing to properties of our entityType, navigationProperties can be used as path too.

With annotations added filtering again in the UI will list items successfully:

Notice that no additional View code had to be written, only adding annotations to our model.

Self test: add or remove some more columns by adding/removing data fields under LineItem annotation and observe smartTable changing according to it

Example2: Adding Value Help to SmartFilterBar control

Given the following model with Products and Categories entitySets:

<EntityType Name="Product">
    <Key>
        <PropertyRef Name="ID"/>
    </Key>
    <Property Name="ID" Type="Edm.Int32" Nullable="false" sap:label="ID"/>
    <Property Name="CategoryID" Type="Edm.Int32" Nullable="false" 
        sap:label="Category"/>
</EntityType>
<EntityType Name="Category">
    <Key>
        <PropertyRef Name="ID"/>
    </Key>
    <Property Name="ID" Type="Edm.Int32" Nullable="false" sap:label="ID"/>
    <Property Name="Name" Type="Edm.String" Nullable="true" 
        sap:label="Name"/>
</EntityType>
<EntityContainer Name="DemoService" m:IsDefaultEntityContainer="true">
    <EntitySet Name="Products" EntityType="ODataDemoService.Product" />
    <EntitySet Name="Categories" EntityType="ODataDemoService.Category" />
</EntityContainer>

and the UI View with smartFilter and smartTable components listing the Products entitySet with a CategoryID filter:

<smartFilterBar:SmartFilterBar id="smartFilterBar" entitySet="Products" 
    useToolbar="false" showFilterConfiguration="false">
    <smartFilterBar:controlConfiguration>
        <smartFilterBar:ControlConfiguration key="CategoryID" 
            visibleInAdvancedArea="true"
            preventInitialDataFetchInValueHelpDialog="false">
        </smartFilterBar:ControlConfiguration>
    </smartFilterBar:controlConfiguration>
</smartFilterBar:SmartFilterBar>
<smartTable:SmartTable id="ProductsSmartTable" entitySet="Products" 
    smartFilterId="smartFilterBar" header="Products" showRowCount="true"
    useExportToExcel="false" useVariantManagement="false" 
    useTablePersonalisation="false" tableType="ResponsiveTable">
</smartTable:SmartTable>

In the UI CategoryID filter has a built in value help. By default, the framework recognizes CategoryID type as described in the metadata model: Int32. Pressing Value Help icon displays value help popup where filter conditions can be defined for CategoryID as Int32 number: (equal to, between, less than, …)

Resolution:

To list an entitySet as possible values for a property, use ValueList annotation. It applies to property or parameter, and specifies how to get a list of acceptable values for such property or parameter: (for documentations see Appendix1: Annotations, on how to add annotations see Appendix2: Adding annotations to metadata)

<Annotations Target="ODataDemoService.Product/CategoryID">
    <Annotation Term="com.sap.vocabularies.Common.v1.ValueList">
        <Record Type="com.sap.vocabularies.Common.v1.ValueListType">
            <PropertyValue Property="CollectionPath" String="Categories"/>
            <PropertyValue Property="Parameters">
                <Collection>
                    <Record Type="com.sap.vocabularies.Common.v1.ValueListParameterOut">
                        <PropertyValue Property="LocalDataProperty" PropertyPath="CategoryID"/>
                        <PropertyValue Property="ValueListProperty" String="ID"/>
                    </Record>
                    <Record Type="com.sap.vocabularies.Common.v1.ValueListParameterDisplayOnly">
                        <PropertyValue Property="ValueListProperty" String="Name"/>
                    </Record>
                </Collection>
            </PropertyValue>
        </Record>
    </Annotation>
</Annotations>

 

By adding ValueList annotation the automatic value help of CategoryID filter lists Categories as possible values for CategoryID:

Example3: Add Value Help as a Dropdown List to SmartFilterBar control

Let’s extend Example2 model with the following SAP Annotations for Product/CategoryID:

sap:value-list=”fixed-values”

It’s a string indicating whether this property has a value list attached:

  • fixed-values – there is a short list of allowed field values that rarely changes over time
  • standard (default) – no restriction on number and volatility of allowed field values

The list of allowed values is provided as a separate entity set that can be found by interpreting the V4-style annotation Common.ValueList on the same property.

(for documentations see Appendix1: Annotations, on how to add annotation see Appendix2: Adding annotations to metadata)

<EntityType Name="Product">
    <Key>
        <PropertyRef Name="ID"/>
    </Key>
    <Property Name="ID" Type="Edm.Int32" Nullable="false" sap:label="ID"/>
    <Property Name="CategoryID" Type="Edm.Int32" Nullable="false" sap:label="Category" 
        sap:value-list="fixed-values"/>
</EntityType>

CategoryID filter changed from standard value help dialog to dropdown list:

Example4: Display Text value instead of ID in Dropdown List

To display a human readable text value instead of ID, a reference can be defined for entityType properties. Let’s try this on Example3, extending our Category/ID property with the following annotation:

sap:text=”Name”

It’s a path expression that identifies a property in the context of the entity type containing a human-readable text for the value of this property.

(for documentations see Appendix1: Annotations, on how to add annotation see Appendix2: Adding annotations to metadata)

<EntityType Name="Category">
    <Key>
        <PropertyRef Name="ID"/>
    </Key>
    <Property Name="ID" Type="Edm.Int32" Nullable="false" sap:text="Name" sap:label="ID"/>
    <Property Name="Name" Type="Edm.String" Nullable="true" sap:label="Name"/>
</EntityType>                

In the UI we see that Name value displayed instead of ID:

Notice that this is just a display option, in the background CategoryID property will be sent to server not the Name property when filtering Products, which is the desired behaviour.

Appendix1: Annotations

Let’s take a look at the following annotations:

SAP Annotations for OData Version 2.0

These annotation are element properties from the http://www.sap.com/Protocols/SAPData namespace. Properties can be added to metadata for elements edm:Schema, edm:EntityContainer, edm:EntitySet, edm:EntityType, edm:Property, edm:NavigationProperty, edm:FunctionImport, edm:Parameter, edm:AssociationSet.
Example: (edm:Property)

<Schema Namespace="ODataDemoService" xmlns="http://schemas.microsoft.com/ado/2007/05/edm"
            xmlns:sap="http://www.sap.com/Protocols/SAPData">
    <EntityType Name="Product">
        <Key>
            <PropertyRef Name="ID"/>
        </Key>
        <Property Name="ID" Type="Edm.Int32" Nullable="false" sap:label="ID" sap:text="Name"/>
        <Property Name="CategoryID" Type="Edm.Int32" Nullable="false" sap:label="Category" sap:value-list="fixed-values"/>
        <Property Name="SupplierID" Type="Edm.Int32" Nullable="false" sap:label="Supplier" sap:value-list="standard"/>
        <Property Name="Name" Type="Edm.String" Nullable="true" sap:label="Name"/>
    </EntityType>
</Schema>

OData 4.0 Vocabularies

These annotations are Annotations elements from the http://schemas.microsoft.com/ado/2008/09/edm namespace.

Annotations elements has to be placed under the same Schema (same Namespace) as the OData service EntityType and EntityContainer elements. Can be placed into the same metadata.xml file or in a separate annotation file.
Example:

<edmx:Edmx xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx"
        xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
        xmlns:sap="http://www.sap.com/Protocols/SAPData" Version="1.0">
    <edmx:DataServices m:DataServiceVersion="2.0">
        <Schema xmlns="http://schemas.microsoft.com/ado/2008/09/edm"
                Namespace="ODataDemoService" xml:lang="en" sap:schema-version="1">
            <Annotations Target="ODataDemoService.Product"
                    xmlns="http://docs.oasis-open.org/odata/ns/edm">
                <Annotation Term="com.sap.vocabularies.UI.v1.LineItem">
                    <Collection>
                        <Record Type="com.sap.vocabularies.UI.v1.DataField">
                            <PropertyValue Property="Value" Path="Name"/>
                        </Record>
                    </Collection>
                </Annotation>
            </Annotations>
        </Schema>
    </edmx:DataServices>
</edmx:Edmx>            

Appendix2: Adding annotations to metadata

Metadata can be extended with annotations in different places of the application.

SAPUI5

In the UI client, the server side metadata descriptor can be extended with OData 4.0 Vocabularies in a separate file. Annotation file then needs to be referenced in manifest:

"sap.app": {
    "dataSources": {
        "mainService": {
            "uri": "/here/goes/your/serviceurl/",
            "type": "OData",
            "settings": {
                "localUri": "localService/metadata.xml",
                "annotations": [
                    "annotation0"
                ]
            }
        },
        "annotation0": {
            "type": "ODataAnnotation",
            "uri": "annotation0.xml",
            "settings": {
                "localUri": "annotation0.xml"
            }
        }
    }
}

With the use of WebIDE annotation file can be generated by right clikcking webapp and select New > “Annotation File”. Annotation file and the reference to the OData service in manifest will be created:

“Annotation Modeler” is a great visual editor for annotations:

ABAP

SAP Annotations

Annotations can be added in the DEFINE method of the Model Provider Extension class MPC_EXT:

DATA:
    lo_annotation   TYPE REF TO /iwbep/if_mgw_odata_annotation,
    lo_property     TYPE REF TO /iwbep/if_mgw_odata_property.

    lo_annotation->add( iv_key = 'semantics' iv_value = 'fixed-values').
    lo_property = model->get_entity_type('Product')->get_property('CategoryID').
    lo_property->set_value_list( /iwbep/if_mgw_odata_property=>gcs_value_list_type_property-fixed_values ).

Vocabularies

Annotations can be added in the DEFINE method of the Model Provider Extension class MPC_EXT:

DATA: 
    lo_ann_target  TYPE REF TO /iwbep/if_mgw_vocan_ann_target,
    lo_annotation  TYPE REF TO /iwbep/if_mgw_vocan_annotation,
    lo_collection  TYPE REF TO /iwbep/if_mgw_vocan_collection,
    lo_record      TYPE REF TO /iwbep/if_mgw_vocan_record,    
    lo_property    TYPE REF TO /iwbep/if_mgw_vocan_property,  
    lo_simp_value  TYPE REF TO /iwbep/if_mgw_vocan_simple_val.

    " annotations for entity type Product
    lo_ann_target = vocab_anno_model->create_annotations_target( 'Product' ).
    lo_ann_target->set_namespace_qualifier( 'ODataDemoService' ).
    
    " Columns to be displayed by default
    lo_annotation = lo_ann_target->create_annotation( iv_term = 'UI.LineItem' ).
    lo_collection = lo_annotation->create_collection( ).

    lo_record = lo_collection->create_record( iv_record_type = 'UI.DataField' ).
    lo_property = lo_record->create_property( 'Value' ).
    lo_simp_value = lo_property->create_simple_value( ).
    lo_simp_value->set_path( 'Name' ).    

CDS

CDS Annotations

@OData.publish: true
define view Z_Products
	as select from Products
{
	key Products.ID,
	@UI: {
		lineItem: {position: 20}
	}
	Products.Name
}

Conclusion

With the use of Smart components UI behaviour can be controlled by metadata annotations without modifing the UI View code. We saw examples of smart features, and ways to extend metadata with annotations.

Regards,

Peter

4 Comments
You must be Logged on to comment or reply to a post.