Wednesday, 11 March 2020

SAPUI5 Smart features controlled by OData Annotations

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
Example2: Adding Value Help to SmartFilterBar control
Example3: Add Value Help as a Dropdown List to SmartFilterBar control
Example4: Display Text value instead of ID in Dropdown List
Appendix1: Annotations
Appendix2: Adding annotations to metadata

Example1: Listing EntitySet with SmartTable


Given the following model with Products EntitySet:

<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:

SAP ABAP Tutorial and Material, SAP ABAP Learning, SAP ABAP Prep, SAP ABAP Certifications, SAP ABAP Exam Prep

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

Resolution:

To select columns for smartTable, use LineItem annotation. 

<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:

SAP ABAP Tutorial and Material, SAP ABAP Learning, SAP ABAP Prep, SAP ABAP Certifications, SAP ABAP Exam Prep

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, …)

SAP ABAP Tutorial and Material, SAP ABAP Learning, SAP ABAP Prep, SAP ABAP Certifications, SAP ABAP Exam Prep

Resolution:

To list an entitySet as possible values for a property, use ValueList annotation. 

<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:

SAP ABAP Tutorial and Material, SAP ABAP Learning, SAP ABAP Prep, SAP ABAP Certifications, SAP ABAP Exam Prep

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.

<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:

SAP ABAP Tutorial and Material, SAP ABAP Learning, SAP ABAP Prep, SAP ABAP Certifications, SAP ABAP Exam Prep

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.

<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:

SAP ABAP Tutorial and Material, SAP ABAP Learning, SAP ABAP Prep, SAP ABAP Certifications, SAP ABAP Exam Prep

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
◉ OData 4.0 Vocabularies – Can be used with OData Version 2.0 service model

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:

SAP ABAP Tutorial and Material, SAP ABAP Learning, SAP ABAP Prep, SAP ABAP Certifications, SAP ABAP Exam Prep

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

SAP ABAP Tutorial and Material, SAP ABAP Learning, SAP ABAP Prep, SAP ABAP Certifications, SAP ABAP Exam Prep

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
}

No comments:

Post a Comment