Technology Blog Posts by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
former_member620231
Participant
14,092

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-servi...

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!
40 Comments
hac
Participant
Great blog Alex ! Thank you for sharing !
Very well written manual! Exactly what I just needed for my Use Case
former_member620231
Participant
0 Kudos
Thank you! I'm glad it helped you.
0 Kudos
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?
former_member620231
Participant
0 Kudos
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
0 Kudos
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?
former_member620231
Participant
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
0 Kudos
Hey Alex,

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


former_member620231
Participant
0 Kudos
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.
0 Kudos
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?
former_member620231
Participant
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.
monokizsolt7
Participant
0 Kudos
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 ?
former_member620231
Participant
0 Kudos
Unfortunately, I don't know what might cause the issue and I do not have access to Cloud Foundry Portal to test it out.
0 Kudos

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

former_member620231
Participant
0 Kudos
Hi kenginee,

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
0 Kudos
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
former_member620231
Participant
0 Kudos
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
0 Kudos
Sure, how can I share it ? could you share your email
former_member620231
Participant
alex.necula@electrocons.ro

I am in the working hours right know but I will try to take a look later today.
Thanks a lot for the blog and all the help, I was able to implement this finally! 😄
0 Kudos
monokizsolt7 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 ?
0 Kudos
alex.necula is there any specific Table or Tcode where I can look for the saved variants ?
former_member620231
Participant
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! 🙂
former_member620231
Participant
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 monokizsolt7 since on the local sandbox it is enabled, but on the Cloud Foundry the system was probably a trial version.

In your case kenginee, 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

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

EmilJ_
Participant
0 Kudos

Hi Alex,

I have slight trouble setting this up, I have added the FilterBar.js file into the webapp folder, does this blog assume you still have all the ushell and personalization code for variance management present?
Or is this a completely different approach and all you need is filterbar in your XML and the FilterBar.js file?

Are you able to show how your XML is set up?

For reference I have my code set up simillar to this blog:
https://blogs.sap.com/2018/07/23/persistent-table-personalization-in-sap-ui5fiori-apps-using-variant...

I can also show you my code if needed.

Thanks

AlexNecula
Active Contributor

Hi Emil,

Sorry for my late reply, I've been pretty busy these days.

This is a completely different approach than the blog you used.

This is how my XML looks like

<mvc:View controllerName="custom.app.controller.Display" xmlns:mvc="sap.ui.core.mvc" displayBlock="true" xmlns="sap.m"
xmlns:core="sap.ui.core" xmlns:fb="sap.ui.comp.filterbar" xmlns:elc="custom.app.controller" xmlns:table="sap.ui.table"
xmlns:smartvariants="sap.ui.comp.smartvariants">
<Page showHeader="false" title="{i18n>appTitle}">
<content>
<elc:FilterBar id="fbIHCRecRep" persistencyKey="custom.app">
<elc:filterGroupItems>
<fb:FilterGroupItem>
<fb:control>
...
</fb:control>
</fb:FilterGroupItem>
</elc:filterGroupItems>
</elc:FilterBar>
...
</content>
</Page>
</mvc:View>

 

Where "custom.app" is the namespace of my application. In this case, I have FilterBar.js in the "controller" folder of my app. You can put pretty much anything in the persistencyKey attribute but it is important to use it.

Note that this only works with an abap backend since you don't do much on the front-end side, it is taken care of automatically in the backend via standard logic.

Hope this helps,

Alex

EmilJ_
Participant
Hi Alex,

Thanks a lot for this, finnaly got it working.

Emil
0 Kudos
Hi Alex,
Excellent blog. I'm trying to implement this functionality but I can't get it to work. Would it be possible for you to share with me an example of how to implement the onFetchData and onApplyData methods? The variants are saved but I am unable to retrieve the filter values and apply them.
Thanks in advance.
AlexNecula
Active Contributor

Hi Cristian,

The code in these methods is completely up to you. You just have to return a JSON structure of your choice in onFetchData and use it in onApplyData.

onFetchData: function () {
// Read the values from the filters and return them in a JSON
return {
MySystemFilter: this._oSystemsFilters.getSelectedKeys() // You should read these values from your filters, in this case this._oSystemFilters would be a multi combo box
};
},

onApplyData: function (oData) {
// Set the values in your filters
this._oSystemsFilters.setSelectedKeys(oData.MySystemFilter);
// Use the values to filter the data in your table
this._oTable.getBinding("items").filter(this._getFilters()); // this._oTable is your table and this._getFilters is your custom function that returns an array of sap.ui.model.Filter objects
}

Hope this helps!

Alex

Alex,

I really appreciate your help. I had misunderstood how the fetch and apply methods work. with your help, I was able to implement the functionality.

Thank you very much.

snsumm
Discoverer
0 Kudos

Do you know what the solution was to this?  I am currently facing the same issue.

 

Update: I was missing a persistence key on my Filter Bar view.  It looks to be working now.

shubham010
Employee
Employee
0 Kudos

Hello Alex,

 

I am new to Smart Filter Bar and wanted to seek your help in this scenario :

When I apply filters they work fine and are also saved in the tags area (Please refer to the image), but those filters become unchecked in smart filter bar value help dialogue box after filtering.

 

Can you please help me out here? what changes can I make so that the tags remain checked (if selected) always.

Please do tell me if you need any more information.

 

Thanks,

Shubham Singh

RodrigoFP
Discoverer
0 Kudos
Great blog.

Thank you so much for all this information.
davidgbarbero
Participant
0 Kudos
Hello, thank you for the detailed blog! I have followed your blog and was able to create/modify/delete variants in one session, and they work as expected, but when I reload the application, those variants are gone, even though they are present in some of the lrep tables in the backend.

How is it possible to retrieve those variants? Isn't it something that should be done automatically by the smartVariantManagement control?

Thank you again!
Dashrath_Singh
Explorer
0 Kudos
Hi David,

You should be able to see your variant in SUI_SUPPORT. Select user layer and provide your user name and application namespace.

Thanks
divyashreer
Employee
Employee
0 Kudos
Hi Alex,

 

We are using the value help for which I need to use getTokens() in my onFetchData method, but with getTokens() saving of variant is failing with the below error message,

'_save' failed: Cloning is only supported for plain objects

could you please help us out here?

Thanks,
Divya
AlexNecula
Active Contributor
0 Kudos
Hi,

I think you are trying to use the tokens themselves which are objects.

You should use the IDs of the selected tokens instead. So after retrieving the tokens, use oToken.getKey() or whatever you have available to get the ID of that entry and use that in the JSON that you return.

Then, in the apply method, create new instances of sap.m.Token based on the IDs that you receive and then use addToken() on your control. Don't forget to clear the current tokens if it's needed.

Hope you find this useful.

Alex
divyashreer
Employee
Employee
Thank you, it worked!!
divyashreer
Employee
Employee
0 Kudos
Hi,

The load automatically feature is false by default for standard variant but I want it to be true.

Could someone please let me know if executeOnSelection(load automatically) feature is set to true or false by default for standard variant?

Thanks,

Divya

 
Labels in this area