CDS-Fiori Elements – Object Page Facets

SAP Fiori Elements accelerates app development. It’s metadata driven UI design will fast track your app delivery with consistent design pattern. Learn how to design Object Page of Fiori Elements List Report using CDS annotations.

In this blog, I’ll discuss UI.Facet annotation which is used to layout Object Page.

To start with I have 3 CDS views

Material Plant (ZI_MaterialPlantPK)

This CDS view provides a list of material’s plant data with fields from MARC table. We will be using this as a list on Object Page for a selected material. Annotation @UI.lineItem is there to display field as a column on the list.

@AbapCatalog.sqlViewName: 'ZIMATPLNTPK'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Material Plant'
define view ZI_MaterialPlantPK
  as select from I_MaterialPlant
  association [1] to I_Plant    as _Plant    on $projection.Plant = _Plant.Plant
  association [1] to I_Material as _Material on $projection.Material = _Material.Material
{
  key Material,
      @UI.lineItem: [{ position: 10 }]
      @ObjectModel.text.element: ['PlantName']
  key Plant,
      @Semantics.text: true
      _Plant.PlantName,
      @UI.lineItem: [{ position: 20 }]
      @ObjectModel.text.element: ['MRPControllerName']
      MRPController,
      
      @Semantics.text: true
      I_MaterialPlant._MRPController.MRPControllerName ,
      @UI.lineItem: [{ position: 30 }]
      @Semantics.quantity.unitOfMeasure: 'MaterialBaseUnit'
      MaterialSafetyStockQty,
      @Semantics.unitOfMeasure: true
      _Material.MaterialBaseUnit,
      _Plant
}

Material List (ZI_MaterialPK)

This CDS View provides list of Materials. By using CDS View I_Material I am able to pull description of fields like Material, Material Type and Material Group. Later I did join on MARA to bring in some more fields. There is an association to previously defined CDS view ZI_MaterialPlantPK with cardinality one-to-many.

@AbapCatalog.sqlViewName: 'ZIMATPK'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Material'
@VDM.viewType: #BASIC
define view ZI_MaterialPK
  as select from I_Material
    inner join   mara on I_Material.Material = mara.matnr
  association [0..*] to ZI_MaterialPlantPK as _MaterialPlant 
           on $projection.Material = _MaterialPlant.Material
{
  key Material,
      _Text[Language = $session.system_language].MaterialName,
      MaterialType,
      I_Material._MaterialType._Text[Language=$session.system_language].MaterialTypeName,
      MaterialGroup,
      I_Material._MaterialGroup._Text[Language=$session.system_language].MaterialGroupName,
      @Semantics.quantity.unitOfMeasure: 'MaterialWeightUnit'
      MaterialGrossWeight,
      @Semantics.quantity.unitOfMeasure: 'MaterialWeightUnit'
      MaterialNetWeight,
      @Semantics.unitOfMeasure: true
      MaterialWeightUnit,
      mara.aenam as LastChangedby,
      mara.laeda as LastChangedOn,
      _MaterialPlant
}

Consumption View (ZC_MaterialPK)

Consumption view based on ZI_MaterialPK. At the moment, we only have @UI.selectionField, @UI.lineItem and @UI.identification annotation. I have exposed this view with @Odata.publish annotation.

@AbapCatalog.sqlViewName: 'ZCMATPK'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Material Stock'
@VDM.viewType: #CONSUMPTION
@OData.publish: true
define view ZC_MaterialPK
  as select from ZI_MaterialPK
{
      @UI.selectionField: [{ position: 10 }]
      @UI.lineItem: [{ position: 10 }]
      @UI.identification: [{ position: 10 }]
      @ObjectModel.text.element: ['MaterialName']
  key Material,
      @UI.selectionField: [{ position: 20 }]
      @UI.lineItem: [{ position: 20 }]
      @UI.identification: [{ position: 20 }]
      @ObjectModel.text.element: ['MaterialTypeName']
      MaterialType,
      @UI.selectionField: [{ position: 30 }]
      @UI.lineItem: [{ position: 30 }]
      @UI.identification: [{ position: 30 }]
      @ObjectModel.text.element: ['MaterialGroupName']
      MaterialGroup,
      @Semantics.quantity.unitOfMeasure: 'MaterialWeightUnit'
      MaterialGrossWeight,
      @Semantics.quantity.unitOfMeasure: 'MaterialWeightUnit'
      MaterialNetWeight,
      @Semantics.unitOfMeasure: true
      MaterialWeightUnit,
      MaterialTypeName,
      MaterialGroupName,
      MaterialName,
      _MaterialPlant
}

WebIDE Automatically creates Object Page

At this point, if I use the wizard in WebIDE to create Fiori Elements List Report app it will automatically create a default Object Page with CollectionFacet with a ReferenceFacet in it. This is because we do not have @UI.Facet annotation in CDS view and it has to render Object Page. These generated annotations are kept in a local annotation file.

You can have app with just List Report and no object page. I ‘ll cover this in another blog.

With these local auto-generated Facets, object page looks like

Facet: #COLLECTION and #IDENTIFICATION

Basic Object Page

Firstly, we will see CDS annotations required to mimic this basic Object Page facets.

Annotation @UI.Facet starting at line 4 adds Facet of type #COLLECTION. Then annotation staring at line 8 adds a facet of type #IDENTIFICATION_REFERENCE under collection facet. This relation is defined using value parentId.

The facet of type #IDENTIFICATION_REFERENCE displays all fields which are annotated with @UI.identification.

define view ZC_MaterialPK
  as select from ZI_MaterialPK
{
            @UI.facet: [ { id:'idGeneralInformation' ,
                           type: #COLLECTION ,
                           label: 'General Information' ,
                           position: 10 } ,
                         { type: #IDENTIFICATION_REFERENCE ,
                           label : 'General Information',
                           parentId: 'idGeneralInformation' ,
                           id: 'idIdentification' } ]
      @UI.selectionField: [{ position: 10 }]
      @UI.lineItem: [{ position: 10 }]
      @UI.identification: [{ position: 10 }]
      @ObjectModel.text.element: ['MaterialName']
  key Material,
.....

You should normally add these annotations before you generate App in WebIDE. This way WebIDE will not generate local annotations for Object Page. If you are adding these after you have generated the App in WebIDE then you need to delete the local annotations, manually.

Note: Local annotation overrides annotation coming from CDS (external annotation). You can see in screenshot @UI.Facet annotations from CDS are crossed over.

Always add Facets with id. UI Adaptation only works for controls when stable ids are assigned.

Facet: #FIELDGROUP_REFERENCE

When you like to group your fields on Object Page, use Facet type #FIELDGROUP_REFERENCE along with @UI.fieldGroup on field level. This gives you better control over facet type #IDENTIFICATION_REFERENCE.

Adding a Facet of type #FIELDGROUP_REFERENCE under #COLLECTION Facet using parentId annotation. Field group facet defines a unique targetQualifier, all fields which are to be displayed under this facet need to specify this qualifier using annotation @UI.fieldgroup.

Now that we have two facets under one #COLLECTION its best to specify the position of these facets.

define view ZC_MaterialPK
  as select from ZI_MaterialPK
{
      @UI.facet: [      
           { id:'idGeneralInformation' ,
             type: #COLLECTION ,
             label: 'General Information' ,
             position: 10 } ,
           { type: #IDENTIFICATION_REFERENCE ,
             label : 'General Information',
             parentId: 'idGeneralInformation',
             id: 'idIdentification' ,
             position: 10 },
           { type: #FIELDGROUP_REFERENCE ,
             label : 'Last Changed',
             targetQualifier: 'fgLastChanged' ,
             parentId: 'idGeneralInformation' ,
             id : 'idGroupLastChanged' ,
             position: 20 } ]
      
...
      @UI.fieldGroup: [{ qualifier: 'fgLastChanged' , position: 10 }]
      LastChangedby,
      @UI.fieldGroup: [{ qualifier: 'fgLastChanged' , position: 20 }]
      LastChangedOn,
}

If I could try and put that in a picture

Facet: #HEADER

The top area on Object page is reserved for facets defined with purpose #HEADER.

Adding facet with purpose #HEADER, type of this facet is #FIELD_GROUPREFERENCE. So to add fields under this facet we will be using @UI.fieldGroup

define view ZC_MaterialPK
  as select from ZI_MaterialPK
{
      @UI.facet: [
          { id: 'idWeight' ,
             purpose: #HEADER,
             label: 'Weight' ,
             type: #FIELDGROUP_REFERENCE,
             targetQualifier: 'hdWeight'} ,
         { id:'idGeneralInformation' ,
               type: #COLLECTION ,
               label: 'General Information' ,
                position: 10 } ,
             { type: #IDENTIFICATION_REFERENCE ,
               label : 'General Information',
               parentId: 'idGeneralInformation',
               id: 'idIdentification' ,
               position: 10 },
             { type: #FIELDGROUP_REFERENCE ,
               label : 'Last Changed',
               targetQualifier: 'fgLastChanged' ,
               parentId: 'idGeneralInformation' ,
               id : 'idGroupLastChanged' ,
               position: 20 }]
 ..     
      @UI.fieldGroup: [{ qualifier: 'hdWeight' , position: 10 }]
      @Semantics.quantity.unitOfMeasure: 'MaterialWeightUnit'
      MaterialGrossWeight,
      @UI.fieldGroup: [{ qualifier: 'hdWeight' , position: 20 }]
      @Semantics.quantity.unitOfMeasure: 'MaterialWeightUnit'
      MaterialNetWeight,
..
}

You can add more than one header facets, just follow the same annotation. If you have more than one header facets then also specify position to keep them in control.

Facet: #LINEITEM_REFERENCE

to display list on object page we use facet of type #LINEITEM_REFERENCE. You will also need association with cardinality with relation one-to-many which will provide data for list to be displayed.

define view ZC_MaterialPK
  as select from ZI_MaterialPK
{
      @UI.facet: [
         { id: 'idWeight' ,
             purpose: #HEADER,
             label: 'Weight' ,
             type: #FIELDGROUP_REFERENCE,
             targetQualifier: 'hdWeight'} ,
         { id:'idGeneralInformation' ,
               type: #COLLECTION ,
               label: 'General Information' ,
                position: 10 } ,
             { type: #IDENTIFICATION_REFERENCE ,
               label : 'General Information',
               parentId: 'idGeneralInformation',
               id: 'idIdentification' ,
               position: 10 },
             { type: #FIELDGROUP_REFERENCE ,
               label : 'Last Changed',
               targetQualifier: 'fgLastChanged' ,
               parentId: 'idGeneralInformation' ,
               id : 'idGroupLastChanged' ,
               position: 20 },
         { id: 'idLineItemMatPlant' ,
               type : #LINEITEM_REFERENCE ,
               label : 'Plant Data' ,
               position: 20 ,
               targetElement: '_MaterialPlant'}]
..
  
      _MaterialPlant
 }

@UI.Headerinfo

@UI.Headerinfo annotation add list tile, object page title and description. @UI.Headerinfo is added to CDS view before CDS code.

@UI.headerInfo: { typeName: 'Material' ,
                  typeNamePlural: 'Materials' ,
                  title: { type: #STANDARD , value: 'Material'} ,
                  description: { type: #STANDARD, value: 'MaterialName' } }

Consumption View (ZC_MaterialPK) with Annotation

@AbapCatalog.sqlViewName: 'ZCMATPK'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Material Stock'
@VDM.viewType: #CONSUMPTION
@UI.headerInfo: { typeName: 'Material' ,
                  typeNamePlural: 'Materials' ,
                  title: { type: #STANDARD , value: 'Material'} ,
                  description: { type: #STANDARD, value: 'MaterialName' } }
@OData.publish: true
define view ZC_MaterialPK
  as select from ZI_MaterialPK
{
      @UI.facet: [
         { id: 'idWeight' ,
             purpose: #HEADER,
             label: 'Weight' ,
             type: #FIELDGROUP_REFERENCE,
             targetQualifier: 'hdWeight'} ,
         { id:'idGeneralInformation' ,
               type: #COLLECTION ,
               label: 'General Information' ,
                position: 10 } ,
             { type: #IDENTIFICATION_REFERENCE ,
               label : 'General Information',
               parentId: 'idGeneralInformation',
               id: 'idIdentification' ,
               position: 10 },
             { type: #FIELDGROUP_REFERENCE ,
               label : 'Last Changed',
               targetQualifier: 'fgLastChanged' ,
               parentId: 'idGeneralInformation' ,
               id : 'idGroupLastChanged' ,
               position: 20 },
         { id: 'idLineItemMatPlant' ,
               type : #LINEITEM_REFERENCE ,
               label : 'Plant Data' ,
               position: 20 ,
               targetElement: '_MaterialPlant'}]
      @UI.selectionField: [{ position: 10 }]
      @UI.lineItem: [{ position: 10 }]
      @UI.identification: [{ position: 10 }]
      @ObjectModel.text.element: ['MaterialName']
  key Material,
      @UI.selectionField: [{ position: 20 }]
      @UI.lineItem: [{ position: 20 }]
      @UI.identification: [{ position: 20 }]
      @ObjectModel.text.element: ['MaterialTypeName']
      MaterialType,
      @UI.selectionField: [{ position: 30 }]
      @UI.lineItem: [{ position: 30 }]
      @UI.identification: [{ position: 30 }]
      @ObjectModel.text.element: ['MaterialGroupName']
      MaterialGroup,
      @UI.fieldGroup: [{ qualifier: 'hdWeight' , position: 10 }]
      @Semantics.quantity.unitOfMeasure: 'MaterialWeightUnit'
      MaterialGrossWeight,
      @UI.fieldGroup: [{ qualifier: 'hdWeight' , position: 20 }]
      @Semantics.quantity.unitOfMeasure: 'MaterialWeightUnit'
      MaterialNetWeight,
      @Semantics.unitOfMeasure: true
      MaterialWeightUnit,
      @UI.fieldGroup: [{ qualifier: 'fgLastChanged' , position: 10 }]
      LastChangedby,
      @UI.fieldGroup: [{ qualifier: 'fgLastChanged' , position: 20 }]
      LastChangedOn,
      MaterialTypeName,
      MaterialGroupName,
      MaterialName,
      _MaterialPlant
}

16 Replies to “CDS-Fiori Elements – Object Page Facets

  1. Hi CDS Gurus When I try to Implement above example . I am unable to find Interface View I_MaterialPlant, please help me. Currently my system is on SAP ECC ABAP EHP8 7.5

    1. Hi, Thank you for reading this blog.
      I was using S4 1809 system. I understand some of the sap standard CDS are not available in previous versions.

      Could you create you own version of ZI_MaterialPlant with following code

      @AbapCatalog.sqlViewName: ‘ZIMATPLAT’
      @AbapCatalog.compiler.compareFilter: true
      @AbapCatalog.preserveKey: true
      @AccessControl.authorizationCheck: #NOT_REQUIRED
      @EndUserText.label: ‘Material Plant’

      @VDM.viewType: #BASIC
      @ObjectModel.representativeKey: ‘Plant’
      @Search.searchable: true

      define view ZI_MaterialPlant
      as select from marc
      association [1..1] to I_Material as _Material on $projection.Material = _Material.Material
      association [0..1] to I_MRPController as _MRPController on $projection.Plant = _MRPController.Plant
      and $projection.MRPController = _MRPController.MRPController
      association [0..*] to I_MaterialText as _MaterialText on $projection.Material = _MaterialText.Material
      {
      @ObjectModel.foreignKey.association: ‘_Material’
      @ObjectModel.text.association: ‘_MaterialText’
      key marc.matnr as Material,

      @Search.defaultSearchElement: true
      @Search.fuzzinessThreshold: 0.8
      @Search.ranking: #HIGH
      key marc.werks as Plant,

      dispo as MRPController,
      eisbe as MaterialSafetyStockQty,
      lgfsb as DfltStorLocForExtProcmt,

      //ABC Classification
      @ObjectModel.foreignKey.association: ‘_MaterialABCClassification’
      maabc as MaterialABCClassification,
      _MRPController,
      _MaterialText,
      _Material
      }

  2. Hi Pawan , This blog was very helpful for me , Thanks for your efforts.Especially you kept the source code copiable instead of image , so it helped in analyzing and experimenting better.

  3. Hi Pawan,

    I was trying this one, but unable to see the line item on object page!

    i am previewing it directly from eclipse using service bindings.

    Also for UI integration, i only have Visual studio, Can you what could be the problem with our approach.

    Regards

    Krishna

  4. Hello Pawan,

    I am trying a quite similar app as described.

    Going on with second page as plant data for your describing, I was confusing that the “plant data” in my app has navigation property.
    Do you know which attribute may cause such effect?

    You may also need information that my child page external annotation do not contain UI.Facet.ReferenceFacet.target. So it’s quite weried the navigation appears .

    Regards
    Jivens

    1. Hi Pawan,

      I have found another post also by you. #SAP Fiori Elements List Report – Configuring List Navigation
      Question fixed and many thanks.

      B.R. Jivens

  5. Hi Pawan,

    I generate the table successfully both in list page and object page.

    However columns do not use up the available space in both tables.
    We use column widths which sets as percentages or “auto” for resizing one column automatically in freestyle development object. Any annotations or settings in manifast or other approach can handle such attributes for table.

    B.R. Jivens

  6. Hi Pawan,

    I am trying to implementing the list report table generated with unexpected format.

    The columns do not use up the available space.
    In freestyle development, we use element.width = auto or percentage to make column fit to container. And in fiori element , do we have such annotation or settings in manifast or other ways to fix the problems.

    B.R. Hermes

  7. Hi Pawan,

    App generated successfully, but the table in list page and object page do not use up the available space.

    In freestyle development , we use element.width and set to auto or percentage to control this attribute.
    And in fiori element , is there any annotations or settings in manifast or other approach to fix the problem?

    B.R. Hermes

  8. Hi, Thanks for this post. I have one question. I am sure keeping these all annotation in CDS View will be difficult. I know Metadata Extension to keep it separate. But still I wounder is it possible to keep Header and Item(Object) Page annotation in separately and link them with some code. Second, In real time do we really use these kind of annotation ?

Leave a Reply