Technical Articles
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.
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.
- There is not such method as addControl, use setControl
oPersInfo.setControl(this);
- Use this._oSmartVM because there is no variable called oSmartVM.
this._oSmartVM.addPersonalizableControl(oPersInfo);
- 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!
Great blog Alex ! Thank you for sharing !
Very well written manual! Exactly what I just needed for my Use Case
Thank you! I'm glad it helped you.
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?
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.
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
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?
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.
Thank you,
Alex
Hey Alex,
it seems that the updated method works for the initialising process but i am still facing the issue with "LrepConnector"
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.
Alright Alex Necula , to get my knowledge right of understanding how variants a saved:
Variants are saved through
which is independent from the used Abap-database?
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.
Alex Necula is there any specific Table or Tcode where I can look for the saved variants ?
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! 🙂
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 ?
Unfortunately, I don't know what might cause the issue and I do not have access to Cloud Foundry Portal to test it out.
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.
It is hidden when I run on local FLP but it becomes visible when I deploy to Abap repository FLP. Any clue why ?
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
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
BR, Krisha
Hi Alex Necula ,
I am getting following error.
My filterbar xml looks like below :-
Namespace (I have FilterBar.js under controls folder of project)
XML -
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
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.
Please try this and let me know if it's working.
Alex
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
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
Sure, how can I share it ? could you share your email
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! 😀
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.
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-management-step-by-step-guide/
I can also show you my code if needed.
Thanks
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
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
Hi Alex,
Thanks a lot for this, finnaly got it working.
Emil
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.
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.
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.
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
Great blog.
Thank you so much for all this information.