Virtual elements als Filter für Fiori Apps

Eine Fiori Elements ListReport Applikation bietet auf der Listenansicht eine Filterleiste. Hierüber kann der Listenumfang basierend auf Filterausdrücken eingeschränkt werden. Im Filter werden allerdings nur Spalten angeboten, welche in der zugrundeliegenden CDS View (konrekt in der root view) angeboten werden.

Mittels virtual elements kann man darüber hinaus auch Filter zu Spalten anbieten, die nur in einer Assoziation der root view enthalten sind.

Beispiel

Eine Fiori Anwendung zeigt eine Liste mit Auftragsvorgängen an. Durch Navigation in den Detail-Bereich werden zu dem gewählten Vorgang weitere Daten, etwa eine Liste der Materialkomponenten, angezeigt. Auf der Listübersicht soll eine Möglichkeit geschaffen werden, nur Aufträge zu selektieren, deren Materialkomponenten einen bestimmten Sortierbegriff haben.

Lösung

Die Fiori Anwendung ist eine ListReport App, welche mittels Fiori Elements erstellt wurde. Zur Definition der App wurde eine Reihe von CDS Views verwendet. Die Spalte SortField, nach der in der App gefiltert werden soll, befindet sich in der CDV View Entity ZI_PP_COMPONENTS, welche als Composition von der Root View Entity ZI_PP_OVERVIEW verwendet wird (1).

Zunächst muss die Spalte in der ZI_PP_COMPONENTS View definiert werden

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Auftragsvorratsliste VorgangsKomponenten'
@Metadata.allowExtensions: true
define view entity ZI_PP_COMPONENTS
  as select from
  association to parent ZI_PP_OVERVIEW as _...{

  key Reservation,
  key ReservationItem,

      SortField,

    }

Anschließend wird für diese Spalte in der root view ZI_PP_OVERVIEW ein virtual Element wie folgt definiert (2)

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Arbeitsplatzvorratsliste'
@Search.searchable: true
@Metadata.allowExtensions: true
@ObjectModel.semanticKey: ['ManufacturingOrder', 'ManufacturingOrderOperation']
define root view entity ZI_PP_OVERVIEW
  as select from

  composition [0..*] of ZI_PP_COMPONENTS  as _Components

{

        @Search.defaultSearchElement: true
  key   I_ManufacturingOrder.ManufacturingOrder               as ManufacturingOrder,


        @ObjectModel.readOnly:true
        @ObjectModel.virtualElement: true
        @ObjectModel.filter.transformedBy :'ABAP:ZCL_VE_PP_SORTF'
        @EndUserText.label: 'Sortierbegriff'
        cast( '          ' as sortp  )    as sortf_filter,


}

Das Feld sort_filter wird später den eigentlichen Filter auf dem UI bereitstellen. Durch @ObjectModel.virtualElement wird das virtuelle element erzeugt. Über @ObjectModel.filter.transformedBy wird die ABAP Klasse definiert, welche etwaige Filterausdrücke generieren wird.

Dazu muss die Klasse ZCL_VE_PP_SORTF das Interface if_sadl_exit_filter_transform implementieren. In der Methode wird zunächst ein einfacher Filter für das Element _COMPONENTS.SORTFIELD erzeugt. Dabei handelt es sich um den Namen der Komposition (also der child entity) sowie das Feld in der child entity. (3).

Eine beispielhafte Implementierung kann wie folgt aussehen

CLASS zcl_ve_pp_sortf DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
    INTERFACES if_sadl_exit .
    INTERFACES if_sadl_exit_filter_transform .
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.


CLASS zcl_ve_pp_sortf IMPLEMENTATION.
  METHOD if_sadl_exit_filter_transform~map_atom.
    DATA(lo_sortf) = cl_sadl_cond_prov_factory_pub=>create_simple_cond_factory( )->element( '_COMPONENTS.SORTFIELD' ).

    CASE iv_operator.

      WHEN if_sadl_exit_filter_transform~co_operator-equals.

        ro_condition = lo_sortf->equals( iv_value ).

      WHEN if_sadl_exit_filter_transform~co_operator-less_than.
        RAISE EXCEPTION TYPE cx_sadl_exit_filter_not_supp.

      WHEN if_sadl_exit_filter_transform~co_operator-greater_than.
        RAISE EXCEPTION TYPE cx_sadl_exit_filter_not_supp.

      WHEN if_sadl_exit_filter_transform~co_operator-is_null.
        ro_condition = lo_sortf->is_null( ).

      WHEN if_sadl_exit_filter_transform~co_operator-covers_pattern.
        ro_condition = lo_sortf->covers_pattern( iv_value ).

    ENDCASE.
  ENDMETHOD.
ENDCLASS.

Im Beispiel werden nur Filterausdrücke mit = (equals) sowie Mustern (ABC*) implementiert. Filterausdrücke mit einem Vergleich auf größer oder kleiner sind bei dem verwendeten Feld nicht sinnvoll, da es sich um ein CHAR10 Feld handelt.

In der Projection View ZC_PP_OVERVIEW wird nun lediglich das Feld sort_filter als verfügbare Spalte aufgenommen (4)

@EndUserText.label: 'Arbeitsplatzvorratsliste Projection View'
@AccessControl.authorizationCheck: #CHECK
@Metadata.allowExtensions: true
@Search.searchable: true
define root view  entity ZC_PP_PRODUCTIONORDERPOOL as projection on ZI_PP_PRODUCTIONORDERPOOL {
    key ManufacturingOrder,

    sortf_filter,

}

Abschließend wird in der Metadata extension ebenfalls das Feld aufgenommen. Durch entsprechende UI Annotationen wird das Feld als Selektionsfeld ausgegeben (5)

@Metadata.layer: #CORE
@UI.headerInfo: {
                  typeNamePlural: 'Auftragsvorgänge',
                  typeName: 'Auftragsvorgang',
                  title : { value : 'ManufacturingOrder',
                            type : #STANDARD },
                  description: { value : 'ManufacturingOrderOperation',
                                 type: #STANDARD}
                   }

@UI.lineItem: [{criticality: 'XY'}]
annotate view ZC_PP_OVERVIEW with
{

  @Consumption.semanticObject: 'ProductionOrder'
  @UI.selectionField: [{ position: 20 }]
  @UI.lineItem: [{ position: 10},
                 {  type: #FOR_INTENT_BASED_NAVIGATION, semanticObjectAction: 'displayFactSheet' }]
  @EndUserText.label: 'FE-AUF'
  @Consumption.valueHelpDefinition: [ { entity: { name: 'I_MfgOrderStdVH', element: 'ManufacturingOrder' } } ]
  ProductionOrder;
 
  @UI.selectionField: [{position: 40 }]
  sortf_filter;

  @UI.lineItem: [{ position: 40 }]
  @UI.fieldGroup: [{ qualifier: 'OperationDetails', position: 10 }]
  @Consumption.valueHelpDefinition: [{ entity : {name: 'I_ProductVH' , element: 'Product' } } ]
  Product;


}

Ergebnis