SAP Featured Image

In this blog post I will demonstrate the possibility of hiding crosstab columns with the combination of css and scripting.

Use case:

The same 1 datasource is used in several different crosstabs, but in each crosstab different measures should be shown.

As of DS 1.3 this is not yet possible the desired way. The desired way would be a straightforward way with very little scripting involved similarly as it is currently possible to display different measures in different charts by using only 1 datasource by using the “setDataSelection(selection)” method (available only for the Chart component). Till DS 1.4 where this or a similar method will hopefully come for the Crosstab component too, I created a little scenario which enables the showing/hiding of table columns by using the combination of css and scripting to assign the desired css classes to the crosstab(s).

One could create several copies of the same datasource in Design Studio which use only the needed measures, but this is not good for the performance of your bi app.

First some gif image demonstration (click on the image to see the gif animation):

Using radio buttons to display 1 specific measure column or all columns:

Screencast_RadioButtons_5s_630x314_10fps.gif

Using checkboxes to display any combination of measure columns:

Screencast_Checkboxes_5s_628x314_10fps_v2.gif

I really liked the gif image demonstration that Mike used in his post, so I asked him (in one of the comments) how he created it. In the end I ended up with a different set of tools: first I used “Screencastify (Screen Video Recorder)” a Google Chrome extension to capture a screencast in the file format .webm (I captured it at 10fps, video length 5s, screen size of chrome browser ca. 630x320px) in order to get the final gif image fit into the max. 1MB size limitation in scn. And in the 2nd step I converted the .webm file into a .gif file using an online conversion tool.

CSS + Scripting behind my sample:

My bi app has the following components (outline):

/wp-content/uploads/2014/07/outline_507085.png

I’m using a custom css file “customCSS.css”. This is assigned under: Application Properties > Display > Custom CSS. You can of course name it however you want as long your operating system permits the file name. Just don’t forget that the file needs to have the extension .css

/wp-content/uploads/2014/07/custom_css_file_507140.png

APPLICATION > On Startup > contains the following 2 lines of code:

CROSSTAB_1.setCSSClass("showMeasure measure1");
CROSSTAB_2.setCSSClass("showMeasure measure1");

I have set in both the radiobuttongroup and checkboxes that the 1st one should be selected/checked by default and thus I also assigned the appropriate custom css classes to the crosstabs so that only the measure is shown that matches the selected/checked radio button/checkbox.

First let us look more closely how the RADIOBUTTONGROUP_1 works:

Properties:

/wp-content/uploads/2014/07/radiobuttons1_507098.png

Items:

/wp-content/uploads/2014/07/radiobuttons2_507099.png

Events > On Select > has 1 line of code:

/* assign css class to crosstab */
CROSSTAB_1.setCSSClass("showMeasure " + RADIOBUTTONGROUP_1.getSelectedValue());
/*
* delete css classes from crosstab if one of the css classes is "measureAll", no css class means nothing will be hidden
*
* but since the css definition for the measureAll css class is: ".showMeasure.measureAll {}"
* it is not needed to delete any css classes, because the css class doesn't hide anything = shows everything
*/
/* if (Convert.indexOf(CROSSTAB_1.getCSSClass(), "measureAll") > -1) {CROSSTAB_1.setCSSClass("");} */

With this code we assign 2 css classes at once to the crosstab component.

The following 4 combinations are possible in this example:

  • showMeasure measure1 (in the custom css file the definition becomes: .showMeasure.measure1)
  • showMeasure measure2 (in the custom css file the definition becomes: .showMeasure.measure2)
  • showMeasure measure3 (in the custom css file the definition becomes: .showMeasure.measure3)
  • showMeasure measureAll (in the custom css file the definition becomes: .showMeasure.measureAll)

So the “Value” of a radio button is the name of a custom css class.

So once a user clicks on the for example “Measure 2” radio button, the crosstab component gets the following 2 classes assigned:

/wp-content/uploads/2014/07/radiobuttons3_507100.png

And the last step is that the browser reads and interprets the css definitions that are defined in the custom css file, which tell the browser to hide some of the table columns: {display: none;}

The full CSS coding is at the end of this post and also included as attachment.

It is not necessary to assign 2 css classes at once, but it demonstrates at least that it is possible. It added a little bit of clarity for me at the time of creating it and I thought I might need it on the way to the solution so I just might incorporate this logic from the very beginning and see where it takes me. So I use the assignment of 2 css classes instead of just one but it is not a must.

Second let us look more closely how the CHECKBOXES work:

The first checkbox is set to Selected = true, the others are set to false (in my example):

/wp-content/uploads/2014/07/checkboxes1_507141.png

All 3 of them have the same 1 line of code in the Events > On Click event:

BUTTON_1.onClick();

I wrote some universal code so that it can be maintained in 1 place only and so I doesn’t have to repeat it in each checkbox again and again.

What I could have done to save me a probably unnecessary BUTTON component is: I could have written/moved the code from BUTTON_1 into CHECKBOX_1 and then from the other checkboxes I could have used the code: “CHECKBOX_1.onClick();” instead of “BUTTON_1.onClick();”.

Here is the code in the BUTTON_1 component which the checkboxes are using to determine which css classes need to be assigned to the crosstab:

// Determine which checkboxes are checked and assign a css class accordingly to show/hide measures in the crosstab
var checked = "";
if (CHECKBOX_1.isChecked()) {checked = "1";}
if (CHECKBOX_2.isChecked()) {checked = checked + "2";}
if (CHECKBOX_3.isChecked()) {checked = checked + "3";}
//if (checked == "") {checked = "0";} // show no measures at all
if (checked == "") {checked = "All";} // show all measures in the assigned data source
CROSSTAB_2.setCSSClass("showMeasure measure" + checked);

With this code we assign 2 css classes at once to the crosstab component.

The following 8 combinations are possible in this example:

  • showMeasure measure1 (in the custom css file the definition becomes: .showMeasure.measure1)
  • showMeasure measure2 (in the custom css file the definition becomes: .showMeasure.measure2)
  • showMeasure measure3 (in the custom css file the definition becomes: .showMeasure.measure3)
  • showMeasure measureAll (in the custom css file the definition becomes: .showMeasure.measureAll)
  • showMeasure measure12 (in the custom css file the definition becomes: .showMeasure.measure12)
  • showMeasure measure123 (in the custom css file the definition becomes: .showMeasure.measure123)
  • showMeasure measure13 (in the custom css file the definition becomes: .showMeasure.measure13)
  • showMeasure measure23 (in the custom css file the definition becomes: .showMeasure.measure23)

So actually all the scripting/coding is doing is just assigning some css classes to some crosstabs. The css is then doing/controlling the showing/hiding of table columns.

So here is the used css:

(this is an example that works in the tested simple scenario, in more complex scenarios you might have to adjust the logic behind the css and/or scripting, but you might have just such a simple scenario as this one, where it definitely might come handy as a possible solution)

For the css to work it is assumed that:

  • you have the same amount of measures in your datasource (the user doesn’t have the ability to select/filter for example through a filterpanel which measures to be filtered out from the datasource, if the user would for example choose to filter the whole datasource to exclude the 2nd measure, then the previously 3rd measure becomes the 2nd one (the order of the measures changes/shifts)… the css doesn’t check measure’s descriptions or technical names, it just shows/hides columns based on their index)
  • you do not change the order of the measures… the css doesn’t check measure’s descriptions or technical names, it just shows/hides columns based on their index

To summarize: you are showing/hiding table columns not based on the names/ids of the measures but only based upon their order in the datasource.

/* usage: hide button containing scripting code */
.customHidden {display: none;}
/* show all measures, nothing is hidden */
.showMeasure.measureAll {}
/* show 0 measures, hide all measure columns */
.showMeasure.measure0 .sapzencrosstab-ColumnHeaderArea td.sapzencrosstab-HeaderCellDefault
,.showMeasure.measure0 .sapzencrosstab-DataArea td.sapzencrosstab-DataCellDefault
{display: none;}
/* show only 1st measure, hide all measure columns except the 1st column with the 1st measure */
.showMeasure.measure1 .sapzencrosstab-ColumnHeaderArea td.sapzencrosstab-HeaderCellDefault:not(:nth-child(1))
,.showMeasure.measure1 .sapzencrosstab-DataArea td.sapzencrosstab-DataCellDefault:not(:nth-child(1))
{display: none;}
/* show only 2nd measure, hide all measure columns except the 2nd column with the 2nd measure */
.showMeasure.measure2 .sapzencrosstab-ColumnHeaderArea td.sapzencrosstab-HeaderCellDefault:not(:nth-child(2))
,.showMeasure.measure2 .sapzencrosstab-DataArea td.sapzencrosstab-DataCellDefault:not(:nth-child(2))
{display: none;}
/* show only 3rd measure, hide all measure columns except the 3rd column with the 3rd measure */
.showMeasure.measure3 .sapzencrosstab-ColumnHeaderArea td.sapzencrosstab-HeaderCellDefault:not(:nth-child(3))
,.showMeasure.measure3 .sapzencrosstab-DataArea td.sapzencrosstab-DataCellDefault:not(:nth-child(3))
{display: none;}
/* show measures: 1 & 2, hide all measures except the first 2 */
.showMeasure.measure12 .sapzencrosstab-ColumnHeaderArea td.sapzencrosstab-HeaderCellDefault:not(:nth-child(-n+2))
,.showMeasure.measure12 .sapzencrosstab-DataArea td.sapzencrosstab-DataCellDefault:not(:nth-child(-n+2))
{display: none;}
/* show measures: 1 & 2 & 3, hide all measures except the first 3 */
.showMeasure.measure123 .sapzencrosstab-ColumnHeaderArea td.sapzencrosstab-HeaderCellDefault:not(:nth-child(-n+3))
,.showMeasure.measure123 .sapzencrosstab-DataArea td.sapzencrosstab-DataCellDefault:not(:nth-child(-n+3))
{display: none;}
/* show measures: 1 & 3, hide measure in the 2nd column and measures from the 4th column onwards */
.showMeasure.measure13 .sapzencrosstab-ColumnHeaderArea td.sapzencrosstab-HeaderCellDefault:nth-child(2)
,.showMeasure.measure13 .sapzencrosstab-ColumnHeaderArea td.sapzencrosstab-HeaderCellDefault:nth-child(n+4)
,.showMeasure.measure13 .sapzencrosstab-DataArea td.sapzencrosstab-DataCellDefault:nth-child(2)
,.showMeasure.measure13 .sapzencrosstab-DataArea td.sapzencrosstab-DataCellDefault:nth-child(n+4)
{display: none;}
/* show measures: 2 & 3, hide measure in the 1st column and measures from the 4th column onwards */
.showMeasure.measure23 .sapzencrosstab-ColumnHeaderArea td.sapzencrosstab-HeaderCellDefault:nth-child(1)
,.showMeasure.measure23 .sapzencrosstab-ColumnHeaderArea td.sapzencrosstab-HeaderCellDefault:nth-child(n+4)
,.showMeasure.measure23 .sapzencrosstab-DataArea td.sapzencrosstab-DataCellDefault:nth-child(1)
,.showMeasure.measure23 .sapzencrosstab-DataArea td.sapzencrosstab-DataCellDefault:nth-child(n+4)
{display: none;}





Also see attached the contents of the css file (inserted in a txt file).

And the contents of my content.biapp file (inserted in a txt file) – you could create a new empty app first, then go into your own content.biapp file in your repository folder and copy the contents (all or the parts that you need) of my file into yours and you should have relatively quickly an example setup for yourself.

I hope someone learned something new from this post.  Read more SAP related blogs here

Leave a Reply