Skip to Content
Technical Articles
Author's profile photo Alex Necula

SAPUI5 FilterBar with SmartVariantManagement

Objective

Create a custom FilterBar extension with a fully working SmartVariantManagement.

It requires minimal effort with the filters data, the rest is already implemented!

Backstory

I had a business requirement to create a custom Fiori app that should have a filter bar with variant management similar to the one from standard SAP applications.

The basic variant management for the FilterBar element (SmartVariantManagementUi2) lacks a lot of features (sharing, transport, apply automatically).

I tried using SmartFilterBar because it already has everything you need in terms of variant management. However, I stumbled upon some problems and also I did not know how to implement certain features so I decided to abandon it.

Then I have found this great blog that talks about creating a custom variant management using sap.ui.comp.variants.VariantManagement.

https://blogs.sap.com/2017/01/12/variant-management-implementation-using-shell-personalization-service/

I have also implemented all the features that the blog did not talk about. However, there are two problems, one is that the element is not IN the filter bar without maybe some workarounds, second is that the sharing (transporting) feature would be implemented manually which is not a comfortable thing to do.

Then this tutorial appeared in my searches. It talks about how to use SmartVariantManagement in the FilterBar. Unfortunately, it was not that straightforward as it says and this is what I will try to cover in this blog.

https://help.sap.com/doc/saphelp_uiaddon10/1.17/en-US/2a/e520a67c44495ab5dbc69668c47a7f/frameset.htm

Implementation

The SAPUI5 version I have worked on is 1.60.

Extend the FilterBar control

There are some modifications of the code from the FilterBar documentation above.

  1. There is not such method as addControl, use setControl
    oPersInfo.setControl(this)​;
  2. Use this._oSmartVM because there is no variable called oSmartVM.
    this._oSmartVM.addPersonalizableControl(oPersInfo);

     

  3. Overwrite the mehod _isTINAFScenario. The original one checks only with this._isUi2Mode() which verifies that the variant management is an instance of SmartVariantManagementUi2. Add a case for SmartVariantManagement.

The FilterBar.js file

Use this the same way you would use the standard FilterBar by including the xml namespace for custom.control.

sap.ui.define([
    "sap/ui/comp/filterbar/FilterBar",
    "sap/ui/comp/smartvariants/PersonalizableInfo",
    "sap/ui/comp/smartvariants/SmartVariantManagement"
], function (FilterBar, PersonalizableInfo, SmartVariantManagement) {
   "use strict";
   
   var CustomFilterBar = FilterBar.extend("custom.control.FilterBar", {
       renderer: function(oRm, oControl) {
           FilterBar.getMetadata().getRenderer().render(oRm, oControl);
       }
   });
   
   /**
    * Initialise variant management control
    * @private
    */
    CustomFilterBar.prototype._initializeVariantManagement = function () {
        if (this._oSmartVM && this.getPersistencyKey()) {
            var oPersInfo = new PersonalizableInfo({
                type: "filterBar",
                keyName: "persistencyKey"
            });
            oPersInfo.setControl(this);

            if (this._oSmartVM._loadFlex) {
                this._oSmartVM._loadFlex().then(function () {
                    this._oSmartVM.addPersonalizableControl(oPersInfo);
                    FilterBar.prototype._initializeVariantManagement.apply(this, arguments);
                }.bind(this));
            } else {
                this._oSmartVM.addPersonalizableControl(oPersInfo);
                FilterBar.prototype._initializeVariantManagement.apply(this, arguments);
            }
        } else {
            this.fireInitialise();
        }
    };
   
   /**
    * Use SmartVariantManagement instead of SmartVariantManagementUi2
    * Activate the public and apply automatically options
    * 
    * @private
    * @returns {sap.ui.comp.smartvariants.SmartVariantManagement} The variant management control
    */
   CustomFilterBar.prototype._createVariantManagement = function () {
       this._oSmartVM = new SmartVariantManagement({
           showExecuteOnSelection: true,
           showShare: true
       });
       
       return this._oSmartVM;
   };
   
   /**
    * The original method accepts only SmartVariantManagementUi2
    * 
    * @private
    * @returns {boolean} Result
    */
   FilterBar.prototype._isTINAFScenario = function() {
       if (this._oVariantManagement) {
           if (!this._isUi2Mode() && !(this._oVariantManagement instanceof SmartVariantManagement)) {
               return true;
           }
       } else {

           /* eslint-disable no-lonely-if */
           // scenario: VH dialog: VM replaced with collective search control
           if (this._oCollectiveSearch && this.getAdvancedMode()) {
               return true;
           }
           /* eslint-enable no-lonely-if */
       }

       return false;
   };
   
   return CustomFilterBar;
});

Events

By going throught the FilterBar source code, I have found that the variant management is never initialised. For this, you have to fire the filter bar’s initialise event manually which also initialises the variant management. I think it is convenient to fire it in the onAfterRendering event of the controller of the view that has the filter bar.

this.getView().byId("filterBarId").fireInitialise();

Register the methods for fetching and applying data. I registered them after calling fireInitialise.

this.getView().byId("filterBarId").registerFetchData(this.onFetchData.bind(this));
this.getView().byId("filterBarId").registerApplyData(this.onApplyData.bind(this));

Write the registered methods

The onFetchData method should return a custom JSON with relevant data for filters.

onFetchData: function () { … }

The onApplyData method gets the above saved JSON as a parameter for the selected variant. Use this to fill the filters.

onApplyData: function (oVariantContent) { … }

These two methods are the only things you have to implement based on your filters.

The options Set As Default, Public (sharing with transports included), Apply Automatically, Add as Favorite, Delete, Rename should work out of the box.

Dirty State

Add this method for whenever the variant should be in modified state, for example in the change event of a DatePicker. In the modified state, an asterisk will appear near the title of the variant and the Save button gets enabled if it’s not the standard variant.

onFilterChange: function () {
    var oVariantManagement = this.getView().byId("filterBarId").getVariantManagement();
    
    if (oVariantManagement) {
        oVariantManagement.currentVariantSetModified(true);
    }
},

Conclusion

This control should make it easy to have a variant management in any freestyle Fiori app used in an SAP system.

I hope it helped you out!

Assigned tags

      25 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Andrei Ciochina
      Andrei Ciochina

      Great blog Alex ! Thank you for sharing !

      Author's profile photo Marian Bauersachs
      Marian Bauersachs

      Very well written manual! Exactly what I just needed for my Use Case

      Author's profile photo Alex Necula
      Alex Necula
      Blog Post Author

      Thank you! I'm glad it helped you.

      Author's profile photo Victor Klein
      Victor Klein

      A very helpfull blog. Big thanks Alex Necula !

       

      But there is one question left for me.

      Is the initialising wirtten in the "onAfterRendering" of the FilterBar.js or in the controller of the view, where i am using the FilterBar?

      This question refers also to "onFetchData"- and "onApplyData"-Function?

      Author's profile photo Alex Necula
      Alex Necula
      Blog Post Author

      Hi Victor,

      Thank you for the appreciation!

      The below code is written in the onAfterRendering of the controller of the view where you are using the FilterBar.

      this.getView().byId("filterBarId").fireInitialise();
      this.getView().byId("filterBarId").registerFetchData(this.onFetchData.bind(this));
      this.getView().byId("filterBarId").registerApplyData(this.onApplyData.bind(this));

       

      onFetchData and onApplyData functions are also written in the same controller.

      Please let me know if there is something else I can help you with.

      Alex

      Author's profile photo Victor Klein
      Victor Klein

      Thanks for that quick response!

       

      Allright, so I implemented it the correct way but i am facing this problem... Did I miss anything from your blog?

      Does this occurs, because the onFetchData and onApplyData are not filled with logic?

      Author's profile photo Alex Necula
      Alex Necula
      Blog Post Author

      Victor,

      It is possible that this error occurs because some libraries are loaded differently in a newer version of SAPUI5. Please replace the method _initializeVariantManagement inside FilterBar.js with the following and let me know if this works so I can update the blog.

      CustomFilterBar.prototype._initializeVariantManagement = function () {
          if (this._oSmartVM && this.getPersistencyKey()) {
              var oPersInfo = new PersonalizableInfo({
                  type: "filterBar",
                  keyName: "persistencyKey"
              });
              oPersInfo.setControl(this);
      
              if (this._oSmartVM._loadFlex) {
                  this._oSmartVM._loadFlex().then(function () {
                      this._oSmartVM.addPersonalizableControl(oPersInfo);
                      FilterBar.prototype._initializeVariantManagement.apply(this, arguments);
                  }.bind(this));
              } else {
                  this._oSmartVM.addPersonalizableControl(oPersInfo);
                  FilterBar.prototype._initializeVariantManagement.apply(this, arguments);
              }
          } else {
              this.fireInitialise();
          }
      };

      Thank you,

      Alex

      Author's profile photo Victor Klein
      Victor Klein

      Hey Alex,

      it seems that the updated method works for the initialising process but i am still facing  the issue with "LrepConnector"

      Author's profile photo Alex Necula
      Alex Necula
      Blog Post Author

      Are you running from a local environment?

      If yes, you might need to modify the proxy path to allow the communication with the backend server for lrep.

      For visual code studio (and probably sap business aplication studio) you should have the backend path of the customMiddleware equal to '/sap' instead of '/sap/opu/odata'.

      For SAP Web IDE you should add the path '/sap/bc/lrep' to neo-app.json.

      If neither of this works, you should still be able to deploy to the SAP system and check there.

      Author's profile photo Victor Klein
      Victor Klein

      Alright Alex Necula , to get my knowledge right of understanding how variants a saved:

      Variants are saved through

      'sap/ui/comp/smartvariants/PersonalizableInfo'

      which is independent from the used Abap-database?

      Author's profile photo Alex Necula
      Alex Necula
      Blog Post Author

      Variants are saved through the ICF path /sap/bc/lrep in the system database.

      sap/ui/comp/smartvariants/PersonalizableInfo is an SAPUI5 control used by the framework in the process of saving. In this case it is used to specify what type of variant is used which is filter bar and what attribute of the control should be used to create the id in the database table which is persistencyKey. persistencyKey is an already existing attribute that was created for the variant management use case.

      Author's profile photo Krisha Engineer
      Krisha Engineer

      Alex Necula is there any specific Table or Tcode where I can look for the saved variants ?

      Author's profile photo Alex Necula
      Alex Necula
      Blog Post Author

      I have worked a little bit with the variants table where I was supposed to copy the variants from a standard app to an extension. I warn you: they are really cumbersome to work with :).

      I don't remember a lot, but I know there were 2 or 3 categories of tables, one that stores transportable data (public), one that stores user-dependent (private) and possibly one more that stores local public data.

      That being said, I think the tables that you are looking for are /UIF/LREPDCONT, /UIF/LREPDCONTCD and /UIF/LREPDCONTCV. They store much more data than variants, so you should look for the following pattern in the NAMESPACE field: apps/<YOUR_APP_ID>/changes/

      Now here comes the hard part: the content is stored in the CONTENT field in hex. After you decode it, you will have a JSON string that you will have to work with.

      You will find additional tables that have LREP in their names if you want to go deeper.

      Good luck! 🙂

      Author's profile photo Zsolt Monoki
      Zsolt Monoki

      This works in my local FLP sandbox, but when I deploy the app to Cloud Foundry Portal service, the "showShare" functionality does not work, the "Public" option does not appear on the UI.

      Any idea why this might be Alex Necula ?

      Author's profile photo Alex Necula
      Alex Necula
      Blog Post Author

      Unfortunately, I don't know what might cause the issue and I do not have access to Cloud Foundry Portal to test it out.

      Author's profile photo Krisha Engineer
      Krisha Engineer

      Zsolt Monoki Alex Necula I am facing the opposite issue. I have showshare="false" in FilterBar.js and I intend to hide the Public checkbox as below.

       CustomFilterBar.prototype._createVariantManagement = function () {
             this._oSmartVM = new SmartVariantManagement({
                 showShare: false
       });

      It is hidden when I run on local FLP but it becomes visible when I deploy to Abap repository FLP. Any clue why ?

      Author's profile photo Alex Necula
      Alex Necula
      Blog Post Author

      I cannot be 100% certain, but after looking through the standard code more, it looks like the showShare property was not meant to be used in the constructor.

      From what I understand, the standard implementation uses the sharing functionality by default. It becomes set to false only if the backend system is marked as Trial. This is determined by a method from sap.ushell.Container. If there is no such object (which usually happens on a local environment) then the default behavior is used (so enabled).

      This makes sense for Zsolt Monoki since on the local sandbox it is enabled, but on the Cloud Foundry the system was probably a trial version.

      In your case Krisha Engineer, the local environment is possibly different. Maybe the linked system to the local environment is different than the abap system you are testing? Or your local environment has some special features (which for me on Visual Studio are not available) and creates a sandbox that is marked as Trial.

      In the end, I don't think there is much to do here unless you decide to use the "setShowShare" method after the default implementation uses it (which btw can be found in SmartVariantManagement.prototype._dataReceived), but that is a little more complicated and imo it's not worth taking down a working feature.

      Alex

      Author's profile photo Krisha Engineer
      Krisha Engineer

      Well, since I only needed to hide the Public checkbox and showShare didn't work, did a workaround and hid it via custom styles.css. If it helps someone 🙂

      div#__management0-share {	
      	display: none;
      }

      BR, Krisha

      Author's profile photo Krisha Engineer
      Krisha Engineer

      Hi Alex Necula ,

      I am getting following error.

      My filterbar xml looks like below :-

      Namespace (I have FilterBar.js under controls folder of project)

      xmlns:filterbar="com.ZPROJECT.controls"

      XML -

      <filterbar:FilterBar id="filterBarId" useToolbar="false" showGoOnFB="true" search="onSearch">
       <filterbar:filterItems>
        <filterbar:FilterItem mandatory="true" label="{i18n>Date}">
         <filterbar:control>
          <DateRangeSelection id="delvDateRange" value="{oSearchModel>/delivery_Date}" 
           displayFormat="dd.MM.yyyy" valueFormat="dd.MM.yyyy" change="handleDateChange"/>
         </filterbar:control>
        </filterbar:FilterItem>
       </filterbar:filterItems>
      </filterbar:FilterBar>

      Do I need to add something in Filterbar.js file to make this work?

      Please help out a newbie! I have never worked with custom controls or Variants before! Any help would be appreciated.

      BR,
      Krisha

      Author's profile photo Alex Necula
      Alex Necula
      Blog Post Author

      Hi Krisha Engineer,

      It looks like your program tries to get a custom implementation of the FilterItem object beacuse you have specified the custom namespace in your code.

      If I remember correctly, only the aggregation should inherit the namespace of the parent. However, the objects can a have a different one.

      Since you do not have any custom implementation for FilterItem, you should use the standard namespace like below.

      <filterbar:FilterBar id="filterBarId" useToolbar="false" showGoOnFB="true" search="onSearch">
       <filterbar:filterItems>
        <sap.ui.comp.filterbar:FilterItem mandatory="true" label="{i18n>Date}">
         <sap.ui.comp.filterbar:control>
          <DateRangeSelection id="delvDateRange" value="{oSearchModel>/delivery_Date}" 
           displayFormat="dd.MM.yyyy" valueFormat="dd.MM.yyyy" change="handleDateChange"/>
         </sap.ui.comp.filterbar:control>
        </sap.ui.comp.filterbar:FilterItem>
       </filterbar:filterItems>
      </filterbar:FilterBar>

      Please try this and let me know if it's working.

       

      Alex

      Author's profile photo Krisha Engineer
      Krisha Engineer

      Alex Necula yes this worked! Thanks! But unfortunately, the Variant doesn't appear and gives below error -


      Also data is not visible from backend. My Filterbar.js file is same as yours and onFetch and onApply without any logic as of yet.

      BR, Krisha

      Author's profile photo Alex Necula
      Alex Necula
      Blog Post Author

      Unfortunately, I don't know what the issue might be without debugging.

      Could you make a minimal project that replicates this issue and send it to me along with the SAPUI5 version?

      Thanks,

      Alex

      Author's profile photo Krisha Engineer
      Krisha Engineer

      Sure, how can I share it ? could you share your email

      Author's profile photo Alex Necula
      Alex Necula
      Blog Post Author

      alex.necula@electrocons.ro

      I am in the working hours right know but I will try to take a look later today.

      Author's profile photo Krisha Engineer
      Krisha Engineer

      Thanks a lot for the blog and all the help, I was able to implement this finally! 😀