Skip to Content

Often, we come to a situation where users request to add personalization on UI table so that they can store the view according to their needs and can easily view the required information at just a click of a button.

We are very familiar with the use of sap.ui.table to show data in tabular form as it is very easy to manipulate this form of UI control, plus it offers a lot of flexibility to cater our needs as well. The only thing that it lacks is the personalization. Of course, the table personalization can be added using sap.ui.table.TablePersoController which can control the visibility of the columns, but they won’t be persistent.

So, in order to make it persistent, we can add VariantManagement control to our FIORI application table along with the TablePersoController and store this information in persistent DB offered by backend ABAP system.

The variant management could look like as below screenshot:

The first thing we need to do here is to add this Variant Management control (sap.ui.comp.variants.VariantManagement) to our UI. The below code snippet can be added in the extension or toolbar (deprecated in the new versions) area of the UI table.

<Table id="DemoTableUI" visibleRowCountMode="Fixed" selectionBehavior="Row" selectionMode="None">
	<extension>
		<m:Toolbar>
			<m:content>
				<!--add variant management-->
				<variant:VariantManagement variantItems="{/VariantList}" select="onSelect" save="onSaveAs" enabled="true" manage="onManage" showExecuteOnSelection="false" showShare="false" id="Variants">
					<variant:variantItems>
						<variant:VariantItem text="{Name}" key="{Key}"/>
					</variant:variantItems>
				</variant:VariantManagement>
			<m:ToolbarSpacer/>
				<m:Button icon="sap-icon://action-settings" press="onPersoButtonPressed" tooltip="Columns Settings"/>
			</m:content>
		</m:Toolbar>
	</extension>

The above code will add the variant management control on the toolbar of the UI Table. The button is added to add the table persco controller which will open up the list of the columns to be set as visible or invisible according to the need.

To setup the variant management and to connect the variant management with the backend system, the ushell services (Unified Shell services) will be used.

the code snapshot is shown below that will invoke the service:

// Peronalisation from ushell service to persist the settings
if (sap.ushell && sap.ushell.Container && sap.ushell.Container.getService) {

	var oComponent = sap.ui.core.Component.getOwnerComponentFor(this.getView());
	this.oPersonalizationService = sap.ushell.Container.getService("Personalization");
	var oPersId = {
			container: "TablePersonalisation", //any
			item: "DemoTableUI"                //any- I have used the table name 
			};
	// define scope 
	var oScope = {
			keyCategory: this.oPersonalizationService.constants.keyCategory.FIXED_KEY,
			writeFrequency: this.oPersonalizationService.constants.writeFrequency.LOW,
			clientStorageAllowed: true
				};
	// Get a Personalizer
	var oPersonalizer = this.oPersonalizationService.getPersonalizer(oPersId, oScope, oComponent);
	this.oPersonalizationService.getContainer("TablePersonalisation", oScope, oComponent)
		.fail(function() {})
		.done(function(oContainer) {
			this.oContainer = oContainer;
			this.oVariantSetAdapter = new sap.ushell.services.Personalization.VariantSetAdapter(this.oContainer);
			// get variant set which is stored in backend
                        this.oVariantSet = this.oVariantSetAdapter.getVariantSet("DemoTableUISet");
			if (!this.oVariantSet) { //if not in backend, then create one
				this.oVariantSet = this.oVariantSetAdapter.addVariantSet("DemoTableUISet");
						}
                        // array to store the existing variants
                        Variants = [];
                        // now get the existing variants from the backend to show as list
                        for (var key in this.oVariantSet.getVariantNamesAndKeys()) {
				if (this.oVariantSet.getVariantNamesAndKeys().hasOwnProperty(key)) {
					var oVariantItemObject = {};
					oVariantItemObject.Key = this.oVariantSet.getVariantNamesAndKeys()[key];
					oVariantItemObject.Name = key;
					Variants.push(oVariantItemObject);
							}
						}
                        // create JSON model and attach to the variant management UI control
                     	this.oVariantModel = new sap.ui.model.json.JSONModel();
			this.oVariantModel.oData.Variants = Variants;
			this.getView().byId("Variants").setModel(this.oVariantModel);
					}.bind(this));
// create table persco controller
			this.oTablepersoService = new TablePersoController({
				table: this.getView().byId("DemoTableUI"),
				persoService: oPersonalizer
				});

			}

 

sap.ushell.Container.getService(“Personalization”) will get the instance of the Personalization service from the unified shell which will be used to store the information to the backend system.

This method uses the container mode to persist the variants. Ultimately, we will add values that need to be stored and save them in the container to persist them.

Since, this gives us an oData service to read and write data, we need to get the instance of the adapter to do so. Thus we create an instance of the variant set adapter.

The other thing to notice is that the we need to create our own variant set name. Here I have used the table ID as the variant set name for simplicity but this is up to you.

this.oVariantSetAdapter.addVariantSet will add the new variant set for the application to the service and persist it in the backend. once, we have this in place, we can CRUD the variants in this variant set.

The Table Perso Controller used here is with the accordance to the table i.e. from the control sap.ui.table.TablePersoController.

The code above will not only invoke the variant management service but also attach the perso controller to the table. The perso controller also uses the unified shell service and renders according to the table instance specified in it.

To use the perso controller, we will add code to open the column list on the press event of the button on the toolbar labelled as settings

onPersoButtonPressed: function(oEvent) {
	this.oTablepersoService .openDialog({
		ok: "this.onPerscoDonePressed.bind(this)"
	});
	this.oTablepersoService ._oDialog.attachConfirm(this, this.onPerscoDonePressed.bind(this));
	},
onPerscoDonePressed: function(oEvent) {
	this.oTablepersoService .savePersonalizations();
	},

 

Thus, on pressing the button, the screen containing the column list will popup from which the user can select or un-select to un-hide or hide the column respectively. All the columns details are coming from the table instance provided to the perso controller.

Now comes the interesting part, saving and managing the variant. Once the user chooses the columns that are to visible including their position, he/she can press the variant management button to save the personalization.

on pressing OK button on the popup, the event handler method “”onSaveAs” will be fired. The below code snapshot will store the variant with the columns settings including the order to the backend

		onSaveAs: function(oEvent) {
			// get variant parameters:
			var VariantParam = oEvent.getParameters();
			// get columns data: 
			var aColumnsData = [];
			this.getView().byId("DemoTableUI").getColumns().forEach(function(oColumn, index) {
				var aColumn = {};
				aColumn.fieldName = oColumn.getProperty("name");
				aColumn.Id = oColumn.getId();
				aColumn.index = index;
				aColumn.Visible = oColumn.getVisible();
				aColumnsData.push(aColumn);
			});

			this.oVariant = this.oVariantSet.addVariant(VariantParam.name);
			if (this.oVariant) {
				this.oVariant.setItemValue("ColumnsVal", JSON.stringify(aColumnsData));
				if (VariantParam.def === true) {
					this.oVariantSet.setCurrentVariantKey(this.oVariant.getVariantKey());
				}
				this.oContainer.save().done(function() {
					// Tell the user that the personalization data was saved
				});
			}
		},

The arguments of this method will provide the Variant information entered by the user. to read, we need to use the method oEvent.getParameters(to get those values.

The above code will store the table’s column information required to be stored in an local array. The important information required are column name, its ID, the index of the column and its visibility. Of course, the other information like sorting etc can also be stored but it is entirely based on the requirement.

The variant created do not store array but store the content as a string, so it is absolutely important to convert the array/object to a string value using JSON.stringify() method.

Once, all the information is written to the variant set, the container can be saved.

One more thing here to notice is that to store values the method setItemValue() is used. to store multiple information to the same variant, this method can be called multiple times with different arguments. for e.g.

this.oVariant.setItemValue(“ColumnsVal1”, JSON.stringify(aColumnsData));

this.oVariant.setItemValue(“ColumnsVal2”, JSON.stringify(aColumnsData));

This is helpful in case to something like a filter value to be stored.

the below snapshot shows the stored variant

When user chooses the variant from the above list, we need to read the service and change the table columns accordingly.. Below is the code to do so:

		onSelect: function(oEvent) {
			var selectedKey = oEvent.getParameters().key;
			for (var i = 0; i < oEvent.getSource().getVariantItems().length; i++) {
				if (oEvent.getSource().getVariantItems()[i].getProperty("key") === selectedKey) {
					var selectedVariant = oEvent.getSource().getVariantItems()[i].getProperty("text");
					break;
				}
			}
			this._setSelectedVariantToTable(selectedVariant);
		},

		_setSelectedVariantToTable: function(oSelectedVariant) {
			if (oSelectedVariant) {
				var sVariant = this.oVariantSet.getVariant(this.oVariantSet.getVariantKeyByName(oSelectedVariant));
				var aColumns = JSON.parse(sVariant.getItemValue("ColumnsVal"));

				// Hide all columns first
				this.getView().byId("DemoTableUI").getColumns().forEach(function(oColumn) {
					oColumn.setVisible(false);
				});
				// re-arrange columns according to the saved variant

				aColumns.forEach(function(aColumn) {
					var aTableColumn = $.grep(this.getView().byId("DemoTableUI").getColumns(), function(el, id) {
						return el.getProperty("name") === aColumn.fieldName;
					});
					if (aTableColumn.length > 0) {
						aTableColumn[0].setVisible(aColumn.Visible);
						this.getView().byId("DemoTableUI").removeColumn(aTableColumn[0]);
						this.getView().byId("DemoTableUI").insertColumn(aTableColumn[0], aColumn.index);
					}
				}.bind(this));
			}
			// null means the standard variant is selected or the variant which is not available, then show all columns
			else {
				this.getView().byId("DemoTableUI").getColumns().forEach(function(oColumn) {
					oColumn.setVisible(true);
				});
			}
		},

To read the variant’s data, this.oVariantSet.getVariant() is called and to convert the stored values back into the array JSON.parse() is used. Once we have this information, we can modify the table and refresh the table control to show that data on the screen.

The variants can be managed as well. on pressing the manage button the user will get a screen like below:

he/she can change the name or delete the variant or can make anyone of them as default. to manage this, the below code snippet will do so:

onManage: function(oEvent) {
	var aParameters = oEvent.getParameters();
	// rename variants
	if (aParameters.renamed.length > 0) {
		aParameters.renamed.forEach(function(aRenamed) {
			var sVariant = this.oVariantSet.getVariant(aRenamed.key),
				sItemValue = sVariant.getItemValue("ColumnsVal");
			// delete the variant 
			this.oVariantSet.delVariant(aRenamed.key);
			// after delete, add a new variant
			var oNewVariant = this.oVariantSet.addVariant(aRenamed.name);
			oNewVariant.setItemValue("ColumnsVal", sItemValue);
		}.bind(this));
	}
	// default variant change
	if (aParameters.def !== "*standard*") {
		this.oVariantSet.setCurrentVariantKey(aParameters.def);
	} else {
		this.oVariantSet.setCurrentVariantKey(null);
	}
	// Delete variants
	if (aParameters.deleted.length > 0) {
		aParameters.deleted.forEach(function(aDelete) {
			this.oVariantSet.delVariant(aDelete);
		}.bind(this));
	}
	//  Save the Variant Container
	this.oContainer.save().done(function() {
		// Tell the user that the personalization data was saved
	});
},

Th event parameter will provide the values of all the data changed on the variant manage screen. The methods called are pretty straightforward to change, delete and make default variant.

The above code is the final piece of the puzzle to be in place for this variant management control to work with the table.

One last important information, the backend service used by ushell personalisation service  is INTEROP with name space /UI2/

I hope the above information helps you and if there are any challenges, please comment .

References:

https://sapui5.hana.ondemand.com/#/api/sap.ui.comp.variants

https://sapui5.hana.ondemand.com/#/api/sap.ushell.services.Personalization

https://help.sap.com/viewer/a7b390faab1140c087b8926571e942b7/1709.001/en-US/755536526c384096b5f37d15a693b98d.html

Many thanks

Rahul

To report this post you need to login first.

6 Comments

You must be Logged on to comment or reply to a post.

  1. Sitakant Tripathy

     

    Hi Rahul,

    excellent blog, manifests ur understanding of the framework and how to play around it..

    One quick question though, you mentioned that the personalisation is not persisted but my understanding was that when  you personalize the table column configurations it is stored as a CDS metadata extensions on the backend for this user and hence should be picked automatically for that user.

    Could you please confirm if that is not the case…

    Also, when preparing the table columns the else block restores all the column to visible, would it overwrite the CDS annotations like hidden if any..

    Regards,

    Sitakant.

    (0) 
    1. Rahul Kumar Sankla Post author

      Hi Sitakant,

      What you are saying is about smart table control wherein you can bind the CDS oData directly with the table. The CDS holds the metadata for columns and annotation modeler is used to display them on the view. also, this control has built-in variant management. So, if you use smart table control, you can just activate the Variant management in the smart table control itself. even if you do that, it will still store the variants in the above mention service (/UI2/INTEROP) but the smart table control will manage it automatically and you need not to write any code to do so.

      Whereas the above content is for sap.ui.table where in there is no built-in variant management functionality and thus we can add the same as mention in the content of the blog.

      Anyhow, it will not going to impact the CDS annotation at all as the annotation is a part of the oData metadata which can’t be changed from UI5 app directly. all these manipulation happen on the app only and is stored separately in the BE.

      I hope I answered your question. write me back if you you have any further queries.

      regards

      Rahul

       

       

       

      (2) 
  2. Sandip Mandal

    Hi Rahul,

       Thanks for writing this good blog and i have few queries related to your blog.

    First, where do we store Variant Items to persist the table personalized data using Variant Management?

    Second, can we use custom or standard OData service(non CDS OData) to store the table personalized data in variants. Moreover, if there is already custom OData service available for custom Fiori Apps, then shall we go ahead to enhance the existing custom OData service or create a new OData service to implement the Variant Management control to persist the table personalized data in back end DB provided by backend ABAP system.

    Lastly, could you please explain the methodology in detail which you had used to bind the variant items so that data is coming from database table.

    Regards,

    Sandip

    (0) 
    1. Rahul Kumar Sankla Post author

      For the first question, the data is stored automatically by the system in the service /UI2/INTEROP as soon as the personalisation service is instantiated. The same service is not just limited to store the variant data, but is used for many different purposes as well.

      Of course, the same data can be stored in a custom non-CDS based oData service but that could  consume a lot of time to design. We should use standard SAP services as much as possible.

      I used a JSON data model to bind variant items. but it is up to you the way you want to bind. You can directly bind the entityset to the control if at all and wherever it is possible.

      I hope I answered your concerns. Write me back if need be.

      (0) 
  3. Lakshman Balanagu

    Nice blog, I am trying to save filter values in Variant. But oEvent.getSource().getVariantItems().length in onSelect() method return zero. Even after adding Variant using onSave() method. Could you please help me.

     

    Thanks,

    Lakshman Balanagu.

    (0) 

Leave a Reply