Quantcast
Channel: SAP BusinessObjects Design Studio
Viewing all 662 articles
Browse latest View live

SAP Design Studio - Showing/Hiding Crosstab Columns with Measures with CSS and Scripting

$
0
0

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):

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

custom_css_file.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:

radiobuttons1.png

Items:

radiobuttons2.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:

radiobuttons3.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):

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

 

Cheers

David


Insides on Bookmarking

$
0
0

Introduction

As there are some questions on this functionality in Design Studio (Bookmarks) I post here some insides on "how this function is working". I hope this helps better in understanding what the technical solution is - and based on this some actions and behavior after save / load bookmark will get clearer.

 

Why bookmarks?

The purpose of bookmarks is to save the visible state of the application for a specific user. In 1.3 release the list of bookmarks is user-specific, but anyone who knows the ID can execute a bookmark of a different user directly in URL (parameter BOOKMARK=<ID>).

 

Technical Background

I do not want to rewrite the blog "Working with Bookmarks in Design Studio 1.3" as this is already containing information on bookmarks. The goal here is to give some technical background to explain the behavior after loading a bookmark.

 

Part 1. Saved Content

Currently the bookmarks are saving ALL information about the current application - this is containing in particular:

  • Data Source(s) (including name, filters, variables, drilldown, expanded hierarchies)
  • all Components which are available in the application - all component properties will be saved as they look like at the time of save

 

In both cases, you can look at the content of BIBOK file contained in the local repository under "/_TECHNICAL_CONTENT/" subfolder. You will see a flat list of components and data sources with something called "INIT_PARAMETERS" which represent the state of the application on runtime. Those init parameters are corresponding to the properties visible in designer and you will find there your all values.

 

Part 2. Saved Parameters / Properties

The saved parameters are containing all available properties - this includes also scripts and content of the components (e.g. list of items in a drop down). This should explain why when you save a bookmark and then add it to some drop down in the application - after load of the same bookmark the dropdown is again empty. It was empty at the time of save and this was saved in the bookmark.

 

Part 3. Load of Bookmarks


Why obsolete bookmarks are not listed?

Technically, it is possible to load also obsolete bookmarks - but, taking into count the facts described in "Part 2", load of such bookmark would reset all changed done in the application to the point of time as the bookmark was saved. Based on this fact, listing of old bookmarks for the end user is not supported.


Loading of an Obsolete Bookmark

The opening of a bookmark via URL (using the shared URL) is loading the bookmark. There are some defined rules how such bookmarks are applied on the application:

  • (1) new components (only positioned on the root container level) in the application are not touched - those will be not affected by loaded bookmark
    • eg. you introduce a component "BUTTON_NEW" on the root container level - it will be available even a bookmark is loaded on the application
    • components which are introduced as children of some panels will be not visible as the parent container does not know about them
  • (2) changed properties of existing component will be overwritten by the loaded content from bookmark
    • eg. you change a property TEXT for component "BUTTON_OLD" to "New Text" from "Old Text" - the changed text property will become "Old Text" as this was the value in the bookmark.
  • (3) deletion of component in the application affected by bookmark - loaded bookmark will create the components again even those were deleted in the application.

 

Part 4. What to do after load of bookmark to propagate correctly the bookmark-dropdown?

After loading a bookmark you have to re-create the dropdown by calling explicitly the scripts to get again the actual list of bookmarks and passing the content to the dropdown. Here you will probably face also the issue with selection of first item - this is currently known issue in drop down which has an event "onSelect", but technically this is an "onChange" event - therefore currently selected index cannot be selected again as it does not change.

 

Part 5. Global Script Variables & Local Script Variables


Why local script variables are lost in script after loading the bookmark?

Load of the bookmark needs to reset the application. For this reason the script will be interrupted and re-run with next statement after the bookmark is loaded. All local variables are not defined anymore and cannot be used in the sequence of calls after bookmark load.

 

What happen to global script variables ?

The global script variables are behaving same as the content of any other component - saved in a bookmark and restored by bookmark load to the value which was saved in the bookmark. This means, you cannot use them to pass some values when loading a bookmark.

 

Part 6. Ideas on how to handle th bookmarks workflows.

 

Solution for dropdown issue. Always add first an entry "- select a bookmark - " to assure that the user can select the "first" entry which is located technically on index 2. (discussed in thread Bookmarks and Selection in Dropdown).

 

Passing Values between bookmark loads.

If you ask yourself how to pass values which you know before loading a bookmark - and you want to use after you loaded a bookmark. As of today you need to save them as a bookmark as well, but not using the bookmark itself, but playing with the title of the bookmark. E.g. save a bookmark giving it a title starting with "|" character - then you can pass some name=values separating by ";" character. after loading the real bookmark, you can again loop on all available bookmarks and re-parse the title of the one starting with "|" character. of course you have immediately to delete it. I will try to make an example of this soon - it is not the best implementation, but the current possibility.


Example: Passing Values on Bookmark Load by Additional Bookmark.

In the attached application (works on 1.3 release, you have to create a folder in local repository "INSIDE_BOOKMARK" and copy the file "content.biapp.txt" into the fodler, renaming to content.biapp) you can find some code which helps you to pass values on bookmark load. It is not a perfect implementation, as this is saving a temporary bookmark (which costs also performance) and reuses the bookmark title to build up a name=value pairs which need to be parsed after load. This implementation is only (as far I know) one possible way to pass values through bookmark load.


Inside_Bookmark.png


Functions.

  • Load All Bookmarks - simple script to reload actual bookmarks and pass into the dropdown box (with additional entry to overcome the selection problem in dropdown box )
  • Personalize Application / Delete Personalization - you can check here what happen when you personalize application and load a bookmark
  • Save Bookmark - taking the text as bookmark description
  • List Of Bookmarks - dropdown with all available bookmarks, only for choice - no event "onSelect"
  • Load Bookmark - Simple case, just load a bookmark
  • Load Bookmark - Use the additional workflow to load a bookmark, but keep some actual values as they are now
  • the input field is just to see what bookmark has loaded - you can put here any text which will help you to recognize the bookmark content, there is no "magic" on this field, it is loaded as it was in bookmark
  • the input field for global script variable (MY_VARIABLE) which can be applied (Apply) and reloaded into the text field (Reload - use this after bookmark is loaded to check actual value). The sctipt variable is then visible in the text with current content "- empty -"
  • the input filed for any content which should be kept on bookmark load - when using the "transfer" variant of load.
  • REUSABLE_SCRIPTS (an hidden panel with 2 checkboxes)

          Inside_Bookmark_Scripts.png

    • the first one is a function which is executed before a bookmark is loaded - it is responsible for collecting the values and save of the temporary bookmark
    • the first one is loading the temporary bookmark and reading its title to parse the values and distribute to corresponding areas (global variables or properties of components)

 

Part 7. Future Direction

Development unit is trying to improve the bookmarking functionality by introduction of an "embedded" bookmarks - which will allow to save only an embedded part of an application. By this handling of the bookmarks will be easier - as the load would not reset completely the application, but would reload only a part of it (e.g. some container). Using that, the saved content can be separated from the scripts and would allow more flexibility in bookmarking.

 

Questions?

In case of any questions, please post - I will update the content.

Design Studio 1.3 Support Package 1 Released

$
0
0

Release Information

Support Package 1 for Design Studio 1.3 release is available for download on Service Marketplace.

 

Included Corrections are listed as links on the release note:

 

     1992588 - Design Studio 1.3: Release Note for Support Package 01

 

The content can be downloaded at Download of Support Packages for Letter "D":

 

SUPPORT PACKAGES AND PATCHES - D

     Support Packages and Patches

          - "D" SBOP DESIGN STUDIO" -> SBOP DESIGN STUDIO 1.3

 

Direct Links are also available in Product Availability Matrix (PAM)

 

In case of any additional questions feel free to ask in this blog.

How to Install 2 Design Studio Releases on One Client (for Testing ONLY!)

$
0
0

Background

You need to make some "upgrade comparison" in local mode and you have only one PC.

 

Scenario

You have already installed older Design Studio Client (eg. 1.2 SP2). Now you would like to install Design Studio 1.3 SP1 in parallel to check if your scenarios are behaving as you expect - you just want to compare how the applications are looking in new release.

 

Solution

First, copy the content of "C:\Program Files (x86)\SAP BusinessObjects\Design Studio" into different folder (eg. c:\DesignStudio\Release12_SP2\) and change the SapDesignStudio.ini file by adding the lines 2 and 3 between "Studio" and "-vmargs" to differentiate the workspace folder:

 

Studio
-data 
C:/<some personal folder>/workspace/ds_<version>
-vmargs    

Then make the upgrade to newer version as usual.

Now you have 2 releases. The new one is your release which you can start from Start Menu, the second you can start manually by executing "SapDesignStudio.exe".

 

Optional

You can execute the "Solution" again on the new version and then you have completely separated versions available on your PC. It allows to have more than 2 releases / versions in parallel.

 

Configuration

in case you have specified fixed port for the local jetty server, assure you have different ports for both releases.

 

Support Warning

This procedure is not supported in productive environment.

Modification of Design Studio Loading Indicator

$
0
0

Purpose

You want to change the default loading indicator of Design Studio


Technical Background

There are 3 loading indicators caused by Design Studio

  • (1) the initial loading indicator before the application will be started. It is a full screen indicator with dark background.

              initialloading.png

  • (2) the between-loading indicator. it is a full screen indicator with transparent background.

              loading.gif

  • (3) the crosstab loading indicator for scrolling. It is smaller indicator in crosstab component with dark background.

              initialloading.png

 

What can be changed easily?

The indicators (2) and (3) can be changed by custom CSS. The indicator (1) canot be changed as the initial html page is static and pre-compiled in JAR file which is delivered in the installer.

 

Example of (2) and (3)

In the attached application (created in DS 1.3 SP1) you can find CSS which will change your loading indicators. The only tricky point is that the image URL needs to be hard coded in CSS, this means you need either to check the URL to the image via developer toolings or use full qualified URL with http://..


Screens after change


    (2) between-loading indicator

          main-loading.png

    (3) crosstab loading indicator (&between-loading indicator)

        main-crosstab-loading.png


The corresponding CSS


    (2) between-loading indicator

 

.customLoadingIndicatorZenClass {  background-image:url('/aad/zen/mimes/LOADING_INDICATOR/ajax-loader.gif');  width: 48px !important;  height: 48px !important;  top: 50% !important;  left:50% !important;  z-index: 100000099; /* optional */
}
/* optional */
.customGlasspaneZenClass {  background: #ffffff !important;  opacity: 0.3 !important;  width: 100% !important;  height: 100% !important;  border: 0px solid !important;  z-index: 100000098;
}
/* optional */
.zenCursorLoadingIndicator-outerDiv {  width: 100% !important;  height: 100% !important;  border: 0px solid !important;  z-index: 100000098;
}

    (3) crosstab loading indicator


.sapzencrosstab-loadingAnimation {  background-image:url('/aad/zen/mimes/LOADING_INDICATOR/ajax-loader.gif');  width: 48px !important;  height: 48px !important;  top: 50% !important;  left:50% !important;
}

How To Use the Example?

The examples above were created in local mode.

You have to create a folder in local repository "LOADING_INDICATOR" and copy there the attached files. The txt extension needs to be removed.

Design Studio SDK - A Tab Strip, Toolbar, Segmented Button, a List Box or a Tile Group?

$
0
0

I blogged a while back about a toolbar component and thought about basically what it really was.  It's a list of items that you need to arrange in a horizontal form.  Key word there was list...  I thought about other forms we see selectors that come in list form and thought it would be interesting to see if we could create all these forms in one component for dealing with the list content and let CSS do its job for controlling the form.

 

I wanted to do this without a framework this time like SAPUI5.  I ended up using jQuery solely for convenience sake but all HTML can be seen in the source code and could be done in it's most raw form.  So with that in mind, my first consideration was the actual HTML structure I'd need to use.

 

Most web designers take the approach of using the <ul> HTML element (a list element for the non-HTMLers reading).  With some CSS, these list elements can take a variety of creative forms, which means our actual JavaScript should be quite lean.

 

I did want to include a few convenience options while I was at it, so while this component *IS* a list builder, it also will accept an optional Sprite Sheet parameter to allow for rendering an icon per-item, if you prefer.  If a sprite sheet is chosen, you do need to tell the component how wide and tall the icon is, and how many show per-row.  It will then take care of the math for you.  I took it one step further and added an ability to set the index of each sprite, in case you want to skip or repeat a certain icon.  I also wanted to allow for skipping an entire item with a visibility flag.  And finally, I added a few basic styling options for text alignment and item widths.  This stuff could have been pushed down to CSS but sometimes it's nice to have it in the property window.

 

So with those functional options mentioned, here's an overview of all of them from a contribution.xml perspective:

 

<property id="titles" type="String" title="Titles"/>    <property id="spriteIDs" type="String" title="Sprite IDs (Optional)"/>    <property id="visibilities" type="String" title="Visibilities (Optional)"/>    <property id="fixedWidth" type="String" title="Fixed Width (-1 for auto)"/>    <property id="spriteSheet" type="Url" title="Icon Sprite Sheet (Optional)"/>    <property id="labelClicked" type="String" title="Selected Label"/>    <property id="onclick" type="ScriptText" title="On Click..." group="Events"/>    <property id="labelPlacement" type="String" title="Label Placement (Relative to Icon)">    <possibleValue>After</possibleValue>    <possibleValue>Before</possibleValue>    </property>    <property id="labelOrientation" type="String" title="Label Orientation">    <possibleValue>horizontal</possibleValue>    <possibleValue>vertical</possibleValue>    </property>    <property id="textAlign" type="String" title="Text Alignment">    <possibleValue>left</possibleValue>    <possibleValue>center</possibleValue>    <possibleValue>right</possibleValue>    </property>    <property id="verticalAlign" type="String" title="Vertical Alignment">    <possibleValue>top</possibleValue>    <possibleValue>middle</possibleValue>    <possibleValue>bottom</possibleValue>    </property>      <property id="spriteSheetPerRow" type="int" title="Icons Sprites Per Row"/>    <property id="spriteSheetHoverOffsetX" type="int" title="Hover Offset X (-1 for no offset)" />    <property id="spriteSheetHoverOffsetY" type="int" title="Hover Offset Y (-1 for no offset)" />    <property id="iconWidth" type="int" title="Icon Width (px)"/>    <property id="iconHeight" type="int" title="Icon Height (px)"/>    <initialization>    <defaultValue property="titles">Facebook,YouTube,Reddit,SAP</defaultValue>    <defaultValue property="labelPlacement">After</defaultValue>    <defaultValue property="labelOrientation">vertical</defaultValue>    <defaultValue property="verticalAlign">top</defaultValue>    <defaultValue property="textAlign">center</defaultValue>    <defaultValue property="fixedWidth">-1</defaultValue>    </initialization>

Nothing too complex going on there.  But a few things to call out...  The 'titles', 'visibilities' and 'spriteIDs' properties are all String, however are meant to be used as arrays/CSV notation.  So if you look at the default value for 'titles', you see a few website titles mentioned there for the sake of illustration.  'visibilities' and 'spriteIDs' are optional CSV strings meant for the visibility and sprite index offsets mentioned earlier.

 

So a quick overview of what a Sprite Sheet looks like for those who haven't seen one:

 

https://raw2.github.com/lukasmartinelli/android-actionbar-icons/master/dist/mdpi/img/actionbar-icons.png

 

Sprite Sheets are meant to allow one image to be used across multiple components.  Kind of like a sticker sheet.  (Kind of.)  So by using this same sprite sheet above, let's make a button bar/tab-strip looking thing component.  We'll use the same sprite sheet mentioned above (URL: https://raw2.github.com/lukasmartinelli/android-actionbar-icons/master/dist/mdpi/img/actionbar-icons.png).  I also set the icon width/height to 32 and set the sprites per row to 12 so that the component can do its math.  I also need to set my Sprite/Icon IDs to certain numbers so I can select the icons that I want.  This is optional and will just count in order otherwise.  As you can see, you can also select a default selected label, and a few alignment properties.

 

By default, this looks a OK, but a little dull:

 

menu1.png

 

This is where the CSS fun kicks in.  I've made a few canned styles available to demonstrate the power that CSS can provide on top of this basic functionality.  Below is an example with the component shown above styled, with 3 additional components with different styles applied (click to enlarge):

 

menu2.png

 

So as you can see in the Outline, this was done with just 4 components, however it can do a lot of work for you if you find yourself in the business of making toolbars by hand or tiles etc.  It can even work as a listbox.  You may also notice that it of course has an on click even which you then write your interactive scripting off of.  When you click an item, it updates a selected label which can then be used in the onclick BIAL code:

 

menu3.png

Will share the full code and deployable version in my Utility Pack shortly.  (Design Studio 1.2/1.3 SDK - Design Studio Utility Pack)

 

Enjoy!

Modification of Design Studio Message Popup

$
0
0

Purpose

You want to change sizes of the message popup for better display of longer messages.

 

Technical Background

Message popup can be modified by use of CSS. You have to overwrite few classes and change the correpsonding sizes.

 

"Default" Size

This is how the default message popup is looking like in notification and with content (27 messages). Message Popup is displaying maximum of 15 messages.

 

Message_Default.png


The corresponding CSS

This example is mainly to adjust the sizes, but you can experiment with different properties.


.sapzenmessageview-PopupContainer {  width: 800px !important;  height: 520px !important;
}
.sapzenmessageview-PopupContainerHeader {  width: 800px !important;
}
.sapzenmessageview-RowRepeaterRow {  width: 800px !important;
}
.sapzenmessageview-RowRepeaterRow>.sapUiBorderLayoutBottom>.sapUiTv {  width: 800px !important;
}
.sapzenmessageview-HeaderText {  /* set to popup size - 60px*/  width: 740px !important;
}

Message Popup After the Change

Message_Resized.png

 

How To Use the Example?

In Attachment you can find the application and CSS file. The examples above were created in local mode.

You have to create a folder in local repository "MESSAGE" and copy there the attached files. The txt extension needs to be removed.

Design Studio SDK: SAPUI5 Icon Component

$
0
0

Idea


The idea was to create a component to show/use very simple SAPUI5 Icons in Design Studio Dashboards. I create this component to learn using the Design Studio SDK.

 

Functionality

 

  • use as button
  • change color (design time and runtime)
  • simple selection of Icons  (design time and runtime)

 

 

Coding

 

First we create the additional properties, this is for selecting icons in design time. You have 3 steps in the js file.

  1. get icons names from iconpool
  2. than create a SAPUI5 VerticalLayout
  3. and generate the Icons over the array you get from step 1


additional_properties_sheet.js

 

sap.designstudio.sdk.PropertyPage.subclass("com.mycompany.icon.IconPropertyPage",  function() {
var that = this;
 var name;
this.init = function() {     jQuery.sap.require("sap.ui.core.IconPool");     // Get Icon Names   var aNames = sap.ui.core.IconPool.getIconNames();   // Create Vertical Layout   var content = new sap.ui.commons.layout.VerticalLayout({   width : "100%"  });   // Generate List with Icons     for (var idx in aNames){    var uri = sap.ui.core.IconPool.getIconURI(aNames[idx]);   icon = new sap.ui.core.Icon(aNames[idx], {    src : uri,              size : '32px',         color : '#000000',         width : '32px',        press: function() { name = this.sId;that.firePropertiesChanged(["iconname"]);}   } );   content.addContent(icon);    }   content.placeAt("content");
 };
 // setter and getter
 this.iconname = function(value) {  if (value === undefined) {   return name;  }  else {   name = value;   return this;  }
 };
});

additional_properties_sheet.html

 

doctype html>
html><head><title>Icon Property Sheet</title><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><script src="/aad/zen.rt.components.sdk/resources/js/sdk_propertysheets_handler.js"></script><script src="/aad/resources/sap-ui-core.js" type="text/javascript" id="sap-ui-bootstrap" data-sap-ui-theme="sap_goldreflection" data-sap-ui-libs="sap.ui.commons"></script><script src="additional_properties_sheet.js"></script></head><script>
new com.mycompany.icon.IconPropertyPage();</script><body class="sapUiBody"><div id="content"></div></body>
html>


After creating the additional properties we create the component.js

 

I use the same SAPUI5 component as in the additional_properties_sheet.js. I set the properties, some setter and getter and that is.

 

sap.designstudio.sdk.Component.subclass("com.mycompany.icon.Icon", function() {
 var that = this;
 var height = '32px';
 var width = '32px';
 var color = '#333333';
 var activeColor = '#333333';
 var activeBackgroundColor = '#FFFFFF';
 var hoverColor = '#eeeeee';
 var isClickable = true; 
 var uri;
 var name;
 this.afterUpdate = function() {  height = this.$().height() + 'px';  width = this.$().width() + 'px';  uri = sap.ui.core.IconPool.getIconURI(name);  if (this.isClickable()){   var icon = new sap.ui.core.Icon('', {    src : uri,                size : height,         color : this.color(),         activeColor : this.activeColor(),         activeBackgroundColor : this.activeBackgroundColor(),         hoverColor : this.hoverColor(),         hoverBackgroundColor : this.activeBackgroundColor(),            width : width,        press: function() { that.fireEvent(["onClick"]);}  } );  } else {   var icon = new sap.ui.core.Icon('', {    src : uri,                size : height,         color : this.color(),         activeColor : this.activeColor(),         activeBackgroundColor : this.activeBackgroundColor(),         hoverColor : this.hoverColor(),         hoverBackgroundColor : this.activeBackgroundColor(),            width : width   });  }  icon.placeAt(this.$());
 };
 // property setter/getter functions
this.color = function(value) {  if (value === undefined) {   return color;  } else {   color = value;   return this;  }
 };
 this.activeColor = function(value) {  if (value === undefined) {   return activeColor;  } else {   activeColor = value;   return this;  }
 };
 this.hoverColor = function(value) {  if (value === undefined) {   return hoverColor;  } else {   hoverColor = value;   return this;  }
 };
 this.activeBackgroundColor = function(value) {  if (value === undefined) {   return activeBackgroundColor;  } else {   activeBackgroundColor = value;   return this;  }
 };
 this.isClickable = function(value) {  if (value === undefined) {   return isClickable;  } else {   isClickable = value;   return this;  }
 };
 this.iconname = function(value) {  if (value === undefined) {   return name;  }  else {   name = value;   return this;  }
 };
});

How does it look in design time

Icon1.PNG

  • you can select a color (icon color, active, background, hover)
  • you can select an icon from the list in the additional properties window
  • you can use the icon as button --> set true to the clickable property

 

I'm coding some methods to set the icon color and change the icon, so you can make very easy an exception icon by using arrows and changing the iconname and color in the runtime.

 

contribution.ztl

/* Returns the current icon name. */
 String getIcon() {*  return this.iconname;
 *}
/* Sets the current icon name.  */
 void setIcon(/* New Icon Name */ String newIconName) {*  this.iconname = newIconName;
 *}
/* Returns the current color of the box. */
 String getColor() {*  return this.color;
 *}
/* Sets the current color of the box. */
 void setColor(/* New color */ String newColor) {*  this.color = newColor;
 *}
 }

 

Now that is it, please give me a feedback. The source is attached, please remove the txt from file and unzip the file.


SDK Tips and Tricks: Resources and Images

$
0
0

There are several questions coming up frequently, both in our SDK workshops and here in the forum. Thus I thought it might be a good idea to blog about some typical problems that you as Design Studio developer could be running into.

 

The first thing I want to write about is the different ways how an SDK component could integrate resources, such as images, CSS files, fonts etc. Probably most components need such resources. Using the resources is just normal HTML and JavaScript, but first we need to differentiate two kinds of resources, depending who defines which file to use:

  • Static resources: Those come with the SDK component and are not intended to be modify by the user of the component. Typical examples are decorations of UI element, e.g. the icon shown on a button.
  • Dynamic resources: Those are defined by the user of a component, e.g. if your component allows the user to define a background image using the Design Studio Properties window.

 

Static Resources

The easiest way to include static resources is a CSS file. The SDK has the <cssInclude> tag where you can reference one or more CSS files that come with your SDK extension. As soon as at least one instance of your SDK component is contained in the app, the CSS file is automatically loaded into the page. How this works is shown in the ColoredBox example – making the box having a black border with rounded corners.

You may have seen that the round boarder is not shown when you drop the first ColoredBox into an empty application. Reason is the feature: The Design Studio runtime analyzes an app before the HTML is produced. It will include all needed JS and CSS files into the header. If the first ColoredBox is added into an empty app, the Runtime will dynamically load the new JS files. But it doesn’t load the new CSS files. Fortunately this issue has been resolved in Design Studio 1.3 SP1, thus with an up-to-date Design Studio you will see round corners from start.

As you can include CSS files, it is also easy to include pictures through CSS: If you e.g. want to modify the ColoredBox into a PictureBox, add a picture into the CSS folder or a subfolder. The reference it with a relative path from the CSS:

tree.png


.coloredBox{

      border-radius:25px;

      border-width:4px;

      border-color:black;

      border-style:solid;

      background-image:url("img/Desert.jpg");

       background-size:100%;

}


To see it running, you also need to remove the “color” property getter/setter, as it would overwrite the background image with a background color.

picturebox.png

 

If you want to select one of multiple static images, best use several CSS classes and modify the assigned class in your JS code.

 

You might ask: “Why not simply use an IMG element?” There are some good reasons to prefer a styled DIV over an IMG:

  • You have a lot of possibilities to control the appearance, sizing etc. of the picture using CSS3 attributes, e.g. background-repeat and background-size.
  • The users of your component can use custom CSS files to replace your images with their own.
  • The URL to your image is relative to the CSS. As Design Studio correctly calculates the URL of the CSS file on all platforms, the exact path the image isn’t something your need to care about.

The last point is somehow the “killer argument” to go with CSS whenever possible. Unfortunately there are situations where you must specify the exact URL of your image or other resource, e.g. in SVG image elements.

Fortunately there is a little trick to calculate the path of anything that comes with your SDK component: As you component.js is loaded into the page with a <script> element, at the moment of loading it is the last <script> tag on the page. This fact can be used to calculate the “src” path:


(function() {

var scriptSrc = $("script:last").attr("src");


sap.designstudio.sdk.Component.subclass("com.sap.sample.coloredbox.ColoredBox", function() {

       this.init = function() {

              var imageUrl = scriptSrc.replace("js/component.js", "css/img/Desert.jpg");

              var image = $("<img/>").width("100%").height("100%").attr("src", imageUrl);

              this.$().append(image);

       };

    });

})();


I have modified the handler to be encapsulated in a self-calling function to get a private variable scope. In the first line I use jQuery to get the“src” attribute of the last script element in a variable. This is later used to calculate the path to the image by replacing the relative path to component.js.

Dynamic Resources

Dynamic resources, e.g. images that the Design Studio user associates with their app, are even more dependent on the Design Studio platform.

In local mode, those resources reside inside of the folder of the app in the local repository. In BIP those resources are global BIP repository object, in NetWeaver there are stored as MIMEs in ABAP and on HANA they are again local repository objects.

Thus the exact URL is not known upfront. Therefore the SDK comes with the property type “Url” that automatically creates the correct URL depending on the platform. Unfortunately we can’t provide picker dialogs for those Url properties in the designer, as the platform handle different resource types quite differently.

 

Thus a good tip of user of an SDK component having Url properties on BIP or NetWeaver:

  1. Add an image component.
  2. Use the Image’s “Image” property picker to upload the image and fill the property correctly.
  3. Copy the property value to the SDK component’s URL property.

Now, what will you do if your SDK component wants to display may different images, e.g. depending on data?

Simply create a “template” Url property and fill it with a path pointing to the place where all the images are located. Then your code can replace the template name with the real name.

 

In our training class sample showing a Scatterplot chart with country maps we used this trick:

First we have the Url property “placeholderimage” with getter/setter:

this.placeholderimage = function(value) {

      if (value === undefined) {

             return _placeholderimage;

   } else {

           _placeholderimage = value;

            returnthis;

   }

};

 

Then we assign the SVG element the URL depending on Country of the data source cell – all using D3.


svgimgs.attr("xlink:href", function(d, index) {

     var dimValForPicture = getTextForDimension(index, _imagedim);

     var url = "";

     if (dimValForPicture && _placeholderimage) {

       url = _placeholderimage.replace("DEFAULT", dimValForPicture);

     }

     return url;

});


In current version of Design Studio, the template image should exist, even it it is not used. Else there will be ugly entries in the log file. In next Design Studio release this should be fixed, so you could make the template URL property hidden and assign a reasonable default value to it.


So far my first SDK blog. If you have any areas of interest, please let me know. I'll try to consider those proposals in my next blog.

Changing Data Source Connections SAP BW - error: failed to instantiate data source

$
0
0

Issue: Changing connections from one System to another.

Possible Error message : failed to instantiate data source

 

Test case Example: You would like to change connections from a previously created Design Studio Application. Your connection was pointing to SAP BW Development and you would like to change the connection to a SAP BW Quality Assurance system.

 

Steps to resolve:

 

1) Open Design Studio

 

Open Design studio.jpg

2) Click on the previously created (I will open Test 4,  screenshot above)

Cancel log in prompts if necessary

 

3) Once opened, click on Data Sources (Circled in red in Screenshot below)

Click on Data Sources.jpg

4) Click and expand on Data binding (Circled in red,  Screenshot below)

Click on Data binding.jpg

 

 

5) Click on the square box (Circled in red and Arrow, Screenshot below)

Click on Connection and square box.jpg

Hope this helps.

 

Regards,

Richard d'Abbadie

Format Dialog for Measures as Application Template

$
0
0

Purpose

if you try to allow end users to change the display setting for measures, try out this dialog template. It changes:

  • scaling factor,
  • number of decimals,
  • negative values display,
  • zero display.

 

It works in any application, connected to DS_1 data source. This time no SDK, just a use of scripting in standard.

Important: you can connect this to a data source with different names, but you would need to catch all places where DS_1 is used and replace.

 

User Interface

This is how it looks like below.

measures_display.png

 

Background

Many options can be changed by using scripting. This dialog is mainly using some script functions to

  • read the dimensions
  • find the "measure" dimension
  • fill in the properties into corresponding drop down boxes
  • apply the changes

 

Framework

This implementation is NON-SDK, this is a simple composition of UI elements and scripts based on default functionality which is in standard. For the dialog I have used the way posted in my other document Design Studio Application with Custom Dialogwhere you can check details on dialogs.

 

It is as usual produced with release 1.3 SP1.

 

Workflow

The entry point is the hidden button "DIALOG_MEASURE_OPEN", there you can check up the onClick() method. Inline comments should give you the idea how it is created.

 

// first set the values you want to edit in the dialog...
// read all dimensions
var dimenstions = DS_1.getDimensions();
var measureDimensionId = "";
dimenstions.forEach(function(dimension, index) {  // find key figures (1.3 solution, language dependent; in 1.4 will bring better function "getMeasureDimension()")  // you need to change for other languages and add your language description for key figures  if(dimension.text == "Key Figures" || dimension.text == "-your-lang-description-" ) {  measureDimensionId = dimension.name;  }
});
//  read first 100 members (should be ok for majority of queries)
var measureDimensionMembers = DS_1.getMembers(measureDimensionId, 100);
// initialize the dropdowns, can be one once, but the code here is not performance critical
DIALOG_MEASURE_DROPDOWN_MEASURES.removeAllItems();
// add initial entry, required to trigger "selected" event, could be changed to auto-select the first measure
DIALOG_MEASURE_DROPDOWN_MEASURES.addItem("N/A", " -- select measure --");
// add all measures
measureDimensionMembers.forEach(function(member, index) {  DIALOG_MEASURE_DROPDOWN_MEASURES.addItem(member.internalKey, member.text);
});
// decimals initialisation
DIALOG_MEASURE_DROPDOWN_DECIMALS.setEnabled(false);
DIALOG_MEASURE_DROPDOWN_DECIMALS.removeAllItems();
DIALOG_MEASURE_DROPDOWN_DECIMALS.addItem("DEFAULT", "Query Default");
DIALOG_MEASURE_DROPDOWN_DECIMALS.addItem("0", "0 (0)");
DIALOG_MEASURE_DROPDOWN_DECIMALS.addItem("1", "0.0 (1)");
DIALOG_MEASURE_DROPDOWN_DECIMALS.addItem("2", "0.00 (2)");
DIALOG_MEASURE_DROPDOWN_DECIMALS.addItem("3", "0.000 (3)");
DIALOG_MEASURE_DROPDOWN_DECIMALS.addItem("4", "0.0000 (4)");
DIALOG_MEASURE_DROPDOWN_DECIMALS.addItem("5", "0.00000 (5)");
DIALOG_MEASURE_DROPDOWN_DECIMALS.addItem("6", "0.000000 (6)");
DIALOG_MEASURE_DROPDOWN_DECIMALS.addItem("7", "0.0000000 (7)");
DIALOG_MEASURE_DROPDOWN_DECIMALS.addItem("8", "0.00000000 (8)");
DIALOG_MEASURE_DROPDOWN_DECIMALS.addItem("9", "0.000000000 (9)");
DIALOG_MEASURE_DROPDOWN_DECIMALS.addItem("10", "0.0000000000 (10)");
// scaling initialisation
DIALOG_MEASURE_DROPDOWN_SCALING.setEnabled(false);
DIALOG_MEASURE_DROPDOWN_SCALING.removeAllItems();
DIALOG_MEASURE_DROPDOWN_SCALING.addItem("DEFAULT", "Query Default");
DIALOG_MEASURE_DROPDOWN_SCALING.addItem("1", "1");
DIALOG_MEASURE_DROPDOWN_SCALING.addItem("2", "10");
DIALOG_MEASURE_DROPDOWN_SCALING.addItem("3", "100");
DIALOG_MEASURE_DROPDOWN_SCALING.addItem("4", "1.000");
DIALOG_MEASURE_DROPDOWN_SCALING.addItem("5", "10.000");
DIALOG_MEASURE_DROPDOWN_SCALING.addItem("6", "100.000");
DIALOG_MEASURE_DROPDOWN_SCALING.addItem("7", "1.000.000");
DIALOG_MEASURE_DROPDOWN_SCALING.addItem("8", "10.000.000");
DIALOG_MEASURE_DROPDOWN_SCALING.addItem("9", "100.000.000");
DIALOG_MEASURE_DROPDOWN_SCALING.addItem("10", "1.000.000.000");
// zero initialisation
DIALOG_MEASURE_DROPDOWN_ZERO.setEnabled(false);
DIALOG_MEASURE_DROPDOWN_ZERO.removeAllItems();
DIALOG_MEASURE_DROPDOWN_ZERO.addItem("DEFAULT", "Query Default");
DIALOG_MEASURE_DROPDOWN_ZERO.addItem("EMPTY_CELL", "Empty Cell");
DIALOG_MEASURE_DROPDOWN_ZERO.addItem("WITHOUT_CURRENCY_UNIT", "Without Currency");
DIALOG_MEASURE_DROPDOWN_ZERO.addItem("CUSTOM", "Custom");
// negative initialisation
DIALOG_MEASURE_DROPDOWN_NEGATIVE.setEnabled(false);
DIALOG_MEASURE_DROPDOWN_NEGATIVE.removeAllItems();
DIALOG_MEASURE_DROPDOWN_NEGATIVE.addItem("LEADING_MINUS", "Leading Minus: -56");
DIALOG_MEASURE_DROPDOWN_NEGATIVE.addItem("PARENTHESES", "Parentheses: (56)");
DIALOG_MEASURE_DROPDOWN_NEGATIVE.addItem("TRAILING_MINUS", "Trailing Minus: 56-");
// fill in the values which are coming from the query, those are not dependent on selected measure
var zeroDisplayValue = DS_1.getZeroDisplay();
var enabledCustom = false;
if(zeroDisplayValue == ZeroDisplay.DEFAULT) {  DIALOG_MEASURE_DROPDOWN_ZERO.setSelectedValue("DEFAULT");
}
if(zeroDisplayValue == ZeroDisplay.CUSTOM) {  DIALOG_MEASURE_DROPDOWN_ZERO.setSelectedValue("CUSTOM");  enabledCustom = true;
}
if(zeroDisplayValue == ZeroDisplay.EMPTY_CELL) {  DIALOG_MEASURE_DROPDOWN_ZERO.setSelectedValue("EMPTY_CELL");
}
if(zeroDisplayValue == ZeroDisplay.WITHOUT_CURRENCY_UNIT) {  DIALOG_MEASURE_DROPDOWN_ZERO.setSelectedValue("WITHOUT_CURRENCY_UNIT");
}
var negativeNumberDisplayValue = DS_1.getNegativeNumberDisplay();
if(negativeNumberDisplayValue == NegativeNumberDisplay.LEADING_MINUS) {  DIALOG_MEASURE_DROPDOWN_NEGATIVE.setSelectedValue("LEADING_MINUS");
}
if(negativeNumberDisplayValue == NegativeNumberDisplay.PARENTHESES) {  DIALOG_MEASURE_DROPDOWN_NEGATIVE.setSelectedValue("PARENTHESES");
}
if(negativeNumberDisplayValue == NegativeNumberDisplay.TRAILING_MINUS) {  DIALOG_MEASURE_DROPDOWN_NEGATIVE.setSelectedValue("TRAILING_MINUS");
}
// initialize the ui elements in dialog
DIALOG_MEASURE_PREVIEW.setVisible(true);
DIALOG_MEASURE_CHECKBOX_APPLY.setChecked(false);
// show the background (styled for "blocking UI")
DIALOG_BACKGROUND.setVisible(true);
// show the dilaog frame
DIALOG_MEASURE.setVisible(true);

Then, the detailed changes are implemented in the onSelect() event in the dropdown "DIALOG_MEASURE_DROPDOWN_MEASURES", here the code

 

var selectedMeasure = DIALOG_MEASURE_DROPDOWN_MEASURES.getSelectedValue();
// apply change
if(selectedMeasure == "N/A") {  DIALOG_MEASURE_DROPDOWN_DECIMALS.setEnabled(false);  DIALOG_MEASURE_DROPDOWN_SCALING.setEnabled(false);  DIALOG_MEASURE_DROPDOWN_ZERO.setEnabled(false);  DIALOG_MEASURE_DROPDOWN_NEGATIVE.setEnabled(false);  DIALOG_MEASURE_DESC.setText("Select Measure...");
} else {  DIALOG_MEASURE_DROPDOWN_DECIMALS.setEnabled(true);  DIALOG_MEASURE_DROPDOWN_SCALING.setEnabled(true);  DIALOG_MEASURE_DROPDOWN_ZERO.setEnabled(true);  DIALOG_MEASURE_DROPDOWN_NEGATIVE.setEnabled(true);  var measureDescription = DIALOG_MEASURE_DROPDOWN_MEASURES.getSelectedText();  DIALOG_MEASURE_DESC.setText("Define the display format for " +  measureDescription);  var scalling = DS_1.getScalingFactor(selectedMeasure);  if(scalling == Scaling.FACTOR_DEFAULT) {  DIALOG_MEASURE_DROPDOWN_SCALING.setSelectedValue("DEFAULT");  }  if(scalling == Scaling.FACTOR_1) {  DIALOG_MEASURE_DROPDOWN_SCALING.setSelectedValue("1");  }  if(scalling == Scaling.FACTOR_10) {  DIALOG_MEASURE_DROPDOWN_SCALING.setSelectedValue("2");  }  if(scalling == Scaling.FACTOR_100) {  DIALOG_MEASURE_DROPDOWN_SCALING.setSelectedValue("3");  }  if(scalling == Scaling.FACTOR_1000) {  DIALOG_MEASURE_DROPDOWN_SCALING.setSelectedValue("4");  }  if(scalling == Scaling.FACTOR_10000) {  DIALOG_MEASURE_DROPDOWN_SCALING.setSelectedValue("5");  }  if(scalling == Scaling.FACTOR_100000) {  DIALOG_MEASURE_DROPDOWN_SCALING.setSelectedValue("6");  }  if(scalling == Scaling.FACTOR_1000000) {  DIALOG_MEASURE_DROPDOWN_SCALING.setSelectedValue("7");  }  if(scalling == Scaling.FACTOR_10000000) {  DIALOG_MEASURE_DROPDOWN_SCALING.setSelectedValue("8");  }  if(scalling == Scaling.FACTOR_100000000) {  DIALOG_MEASURE_DROPDOWN_SCALING.setSelectedValue("9");  }  if(scalling == Scaling.FACTOR_1000000000) {  DIALOG_MEASURE_DROPDOWN_SCALING.setSelectedValue("10");  }  var decimal = DS_1.getDecimalPlaces(selectedMeasure);  if(decimal == -1) {  DIALOG_MEASURE_DROPDOWN_DECIMALS.setSelectedValue("DEFAULT");  }  if(decimal == 0) {  DIALOG_MEASURE_DROPDOWN_DECIMALS.setSelectedValue("0");  }  if(decimal == 1) {  DIALOG_MEASURE_DROPDOWN_DECIMALS.setSelectedValue("1");  }  if(decimal == 2) {  DIALOG_MEASURE_DROPDOWN_DECIMALS.setSelectedValue("2");  }  if(decimal == 3) {  DIALOG_MEASURE_DROPDOWN_DECIMALS.setSelectedValue("3");  }  if(decimal == 4) {  DIALOG_MEASURE_DROPDOWN_DECIMALS.setSelectedValue("4");  }  if(decimal == 5) {  DIALOG_MEASURE_DROPDOWN_DECIMALS.setSelectedValue("5");  }  if(decimal == 6) {  DIALOG_MEASURE_DROPDOWN_DECIMALS.setSelectedValue("6");  }  if(decimal == 7) {  DIALOG_MEASURE_DROPDOWN_DECIMALS.setSelectedValue("7");  }  if(decimal == 8) {  DIALOG_MEASURE_DROPDOWN_DECIMALS.setSelectedValue("8");  }  if(decimal == 9) {  DIALOG_MEASURE_DROPDOWN_DECIMALS.setSelectedValue("9");  }  if(decimal == 10) {  DIALOG_MEASURE_DROPDOWN_DECIMALS.setSelectedValue("10");  }  // prepare current value and preview  var preview = DS_1.getDataAsString(selectedMeasure, {});  if(preview == "") {  preview = "Measure Not in Resultset";  DIALOG_MEASURE_CURRENT.setText(preview);  DIALOG_MEASURE_PREVIEW.setText("");  } else {  if(preview.indexOf("-") == -1 && preview.indexOf("(") == -1) {  var selectedNegative = DIALOG_MEASURE_DROPDOWN_NEGATIVE.getSelectedValue();  if(selectedNegative == "LEADING_MINUS") {  preview = "-" + preview;  }  if(selectedNegative == "PARENTHESES") {  preview = "(" + preview + ")";  }  if(selectedNegative == "TRAILING_MINUS") {  preview = preview + "-";  }  }  DIALOG_MEASURE_CURRENT.setText(preview);  DIALOG_MEASURE_PREVIEW.setText(preview);  }
}

the apply functions for the real changes are implemented as onSelect() event in the corresponding dropdowns. To save code, those are also called when OK button is pressed.

 

Use

You can simply also use this dialog and copy and paste it into the "ADHOC" Application created from the delivered application template. Just copy it to the end and attach from any image / button even (if someone needs help on this I can make this more concrete).

 

When coping, do not forget to transwer also the CSS styles.

 

This is how it should look like in the outline:

 

adhoc.png

 

Source Code & Application

All available in local mode on GitHub:

 

For better download I have created a release of this repository here:


Question / Opinions?

now the content is open for discussion.

Connecting Design Studio to Your ERP/ECC System, Like BW Using Operational Data Providers

$
0
0

First this blog would not have been possible if it weren't for the efforts of SCN community member Raf Boudewijns here: Building your own Embedded Analytics application - part 3.

 

This is on ECC enhancement pack 7.

 

I really hadn't tried activating anything until yesterday.  I had some success activating HCM analytics inside SAP ERP but I am not familiar with that data.  So I successfully activated some of the Financial Enhancement packs and their Operational Data Providers ODP for Accounts Payable (FI-AP) - Operational Data Provisioning for Financials - SAP Library

 

I had some success but not without issues.  It is not working with some tools right now but for sure it is working with Design Studio.  The steps are listed below:

 

Inside ERP I used transaction RSA3 (extractor data):

data.png

That means success, I do have data.

 

Then I create a BEx Query against this data:

bex.png

 

The enhancement pack comes with pre-delivered queries but for this example, I decide not to use them.

 

Then go to Design Studio and use this ECC BEx Query.

ds1.png

 

Here is how it looks at run time:

ds2.png

Of course this is just sample data from the sandbox.  It would be more useful to look at vendor line items and balances by actual vendors and periods but to hide the data I picked something easy.

 

So to me this is interesting in that I am running a BEx query using SAP Business Content (which on the surface looks very good for Accounts Payable) without leaving my ERP system.

 

These are operational data providers (defined here ).  The SAP Help says "You can use Operational Analytics to perform OLAP analyses on the application data locally in the application system"

 

I hope to learn more and get things working soon.

Upcoming Design Studio September Events - ASUG has you covered

$
0
0

http://scn.sap.com/servlet/JiveServlet/showImage/38-112012-519669/ASUG.png

Through both in-person events and webcasts ASUG has you covered in September where Design Studio is concerned.

 

First up, a September 15th webcast:

 

I noticed that there are several questions here on SCN about connecting these tools to HANA.  This webcast was planned by ASUG volunteers back at our planning meeting in January.  The ASUG Dashboarding SIG is supporting this webcast.

 

Then September 22-24 is the ASUG SAP Analytics BusinessObjects Conference in Fort Worth, Texas.  The following sessions are at this conference, related to Design Studio:

 

Design Studio and Dashboards Community Pow Wow

This is with SAP Product Manager David Stocker, who wants to hear from the Dashboard Community to give input on Design Studio.  His abstract promises a sneak peek into Design Studio 1.4.

 

I saw David in person in the Spring and he said he wants to reach out to the Dashboard Community about Design Studio.

 

Building Your Company's Data Visualization Strategy

SAP's Ian MAYOR is providing this session, discussing how SAP Lumira and Design Studio complement your data visualization strategy

 

Design Studio Cookbook for Dashboard Users

Both SAP's Ian Mayor and David Stocker are the speakers for this session.  If you are a Dashboard user, learn some tips on how to use Design Studio in this session.

 

Use Scripting to Control Data Access and Customize Content in Design Studio Applications

SAP Customer BNSF Railway shares how they have used scripting for their Design Studio applications.   They will show how they limit access to their Design Studio applications as well.

 

 

Lastly, September 29th, join ASUG online for Design Studio 1.3 Best Practices with SAP's Jie Deng and David Stocker.  This was another webcast planned by ASUG volunteers in January.

 

If you miss these ASUG sessions in September, we have more planned in October.  On October 6th ASUG has a webcast providing a sneak peek into Design Studio 1.4.

 

We hope to see you online or in-person in September.

Linking Crystal Reports to Design Studio

$
0
0

Accessing Crystal Reports from Design Studio using Report Linking

 

What if there is a client requirement that calls for the powerful visualization of Design Studio and the highly formatted data presentation of Crystal Reports ? Well, the ideal solution for that would be to use Report linking, which is similar to the Report-to-Report Interface functionality in BW.  It is straightforward and very easy to use.

I had a Design Studio dashboard that showcased a lot of KPIs across several views using attractive visualization and a Crystal Report that provided the detailed record of the KPIs across different dimensions. I then added a report launch button to the dashboard, which when clicked, would launch the report, with the parameters passed.

 

Let me walk you through the steps required to achieve this.

Steps for Report Linking

 

To demonstrate the functionality, I created an Accounts Receivable dashboard in SAP Design studio 1.3 SP1. The report was designed using SAP Crystal Reports for Enterprise 4.x.

 

DS_Appln.png

 

To keep things simple, I have used a single value prompt here. But we can add multiple variables with multi-select option as well, to be passed to the report from the dashboard. Here I’ve passed the value “USA” from the dashboard, in the document link of the report.

The document link of the report can be extracted from the BOBJ launch pad. Right click on the document and go to document properties.

 

document link.png

 

The document link, with the parameters concatenated to the link, needs to be passed in the  APPLICATION.OpenNewWindow() function. Following is the code written in the On Click event of my launch button, to open the report in a new window.

 

DS_script_for_jump.png

 

My variable’s technical name is prefixed with an lsS which signifies that I’m passing a single value variable to the report. Listed below is the prefix to be attached to the technical name of the variable depending on the type of variable used.

 

Type of variable

Prefix

Comments

Single value

lsS

 

Multiple value

lsM

 

 

Range

 

lsR

Prompt values have to be specified in [](Square brackets)

Selection variable

lsI ( combined with any of the above)

Can be a single value / multi-value or range that is chosen at runtime

 

Below is a snapshot of the  report launched from Design Studio, filtered on the value of the variable for country, “USA”.

 

CR_jump.png

 

In case the report does not fetch data, check the variable screen to ensure that the prompt value was passed as required.

 

prompt_screen_of_CR.png

 

Points to Note

  • If the launch button has to trigger a report created using Crystal Reports 2011, then you will have to prefix the variable prefix (here ‘lsS’), with an ‘X’.
  • You can use any type of variable you want, in the data source, except for the selection option variable. It does not work well with Crystal Reports.
  • The maximum number of variables that may be passed also depends on the max no of characters that your browser supports. For instance, IE has a max character limit of 2048. One way to cut down on the length is to avoid passing blank values to non-mandatory variables.
  • This feature is only partially mobile device compatible, since the behavior of the APPLICATION.OpenNewWindow() function is unpredictable in iOS 7.
  • This technique is comparable to the RRI functionality in BW. RRI is on the Design studio roadmap, yet to be supported.

Feature Wish List for Commonly Used Design Studio Components

$
0
0

Design Studio is one tool, for which efficient design is critical for good performance. Dashboards that are modified haphazardly, based on changing client requirements tend to fare poorly from the performance standpoint. Given that, it is important to know precisely what each component is capable of doing, its limitations, its impact on performance etc. This knowledge helps immensely during the design phase of the dashboard when finalizing on the components to use.


With that in mind, I have been keeping track of some of the limitations of commonly used components in Design Studio 1.3. Check out the table below for my wish list of features for these components!




Basic Components


Checkbox

 

  • Color changeable for Checkmark / Checkbox
  • Checkbox groups – like the Radio button group

 


Date Field

 

  • Multiple selection option
  • Year field display more than the current +/ - 10 values
  • Range selection option

 



Dropdown

 

  • ‘SetSelectedItem’ when the component is populated from a data source
  • Data filtered by the default value, even during initial load, without requiring calling of OnClick
  • Multi-Select option

 


Formatted Text

  • Word-Wrapping
  • More formatting styles

1.jpg

  • Alignment options like center, left, right
  • Line spacing for paragraphs
  • Scroll bars to avoid long text moving out of the component area

 


Image

 

  • Images loading before background processing is done. There is a work around (watch out for a blog on this!)

 

 



Input Field

 

  • Width adjustable based on the length of input
  • Word Wrap option

2.jpg

  • Input fields for password entry, so option to mask typed text with *
  • Option to set Max / Min limit


 

List Box

  • Ability to show hierarchies

 

 


Analytic Components


Cross Tab

  • Header row freezable
  • Drag drop functionality for characteristics and key figures
  • Option to remove grid lines without having to use CSS / Script



Charts

 

  • Range Slider option
  • An option to sort data inside charts – ascending or descending or by value!

3.jpg

  • More chart types - XY, OHLC, Candlestick, Bullet, Sparkline

 


Container Components

Grid Layout

  • Further subdivision of rows and columns - Only way now is to add another grid inside the row / col

4.jpg

 

Page Book

  • Option to move to first or last page

5.jpg

 

Tab strip

  • Scrollbars

                           

 


"Painting" with Design Studio via CSS3

$
0
0

Purpose

tips how to paint with design studio:

* a line

* a square

* a round

 

Preparation

You need empty application with a panel of your wished size - the size is important as you need later to position the components via coordinates. I call the panel PANEL_CONTENT.

 

Decide if you want to paint from top-left or bottom left. Dependent on this decition, you have to use:

  • setTopMargin() and set LeftMargin()

or

  • setBottomMargin() and set LeftMargin()

for positions of all components.

 

Painting a Line

Position a new Panel in PANEL_CONTENT at your coordinates. Assign the CSS class (which you have to define in your css file) which fulfills the purpose: eg. content-line.

 

css

.content-line {  border-top: 1px solid #cccccc;
}

 

Painting a Square

Position a new Panel in PANEL_CONTENT at your coordinates. Assign the CSS class (which you have to define in your css file) which fulfills the purpose: eg. content-square.

 

css

.content-square {  border-top: 3px solid black;  background-color: red;
}

 

Painting a Round / Circle

Position a new Panel in PANEL_CONTENT at your coordinates. Assign the CSS class (which you have to define in your css file) which fulfills the purpose: eg. content-red-80.

 

css

.content-red-80 {  width: 80px;  height: 80px;  border-radius: 40px 40px 40px 40px;  border: 1px solid #cccccc;  background-color: red;
}

Example

By such tricks you can paint whatever you want, eg...

 

paint.png

 

Manual Data Binding

of course, you can read any data value from DataSource with the function getData() and move the elements by changing the top/bottom and left margins. Buttons in the referenced application are showing how.

 

PANEL_SQUARE.setBottomMargin(PANEL_SQUARE.getBottomMargin()+ 60);


Apllication as Example

Download your application from GitHub:

 

 

I hope you have fun with this content.

How to "paint" a Thermometer Component

$
0
0

Purpose

Visualize Data in Design Studio as "Thermometer" in standard installation - without using an SDK development. This thermometer is for percentage values 0 to 100 %.

 

Basics

This component is created using the tips from my Blog "Painting" with Design Studio via CSS3.

 

Setting Values

There is no automatic connection to any data source, but you can set the values as shown in attached application.

 

     Set the "base line"

     "Base line" is a line which sets some custom value, you can move this between 0 and 100

 

// baseline between 0 and 100
var valueAsIntegerBetween0and100 = Convert.stringToInt(INPUTFIELD_2.getValue());
// baseline is aline positioned in the scale using bottom margin value
PANEL_TH_BASELINE.setBottomMargin(valueAsIntegerBetween0and100 * 2 + 100);

     Set the "value"

     "Value" is the visualization of the current percentage. Between 0 and 100.

 

// here the value must be manually calculated
var valueAsIntegerBetween0and100 = Convert.stringToInt(INPUTFIELD_1.getValue());
// the value is visualized by the hight of the corresponding PANEL
PANEL_TH_VALUE.setHeight(valueAsIntegerBetween0and100 * 2);
// assuming the base line is already set, here the "back" calculation from bottom margin to percentage value
var baselineValueBwtween0and100 = PANEL_TH_BASELINE.getBottomMargin();
baselineValueBwtween0and100 = baselineValueBwtween0and100 - 100;
baselineValueBwtween0and100 = baselineValueBwtween0and100 / 2;
// based on the difference between base line and value, different css styles can be set
if(valueAsIntegerBetween0and100 >= baselineValueBwtween0and100) {  PANEL_TH_BASE.setCSSClass("th-base th-good");  PANEL_TH_VALUE.setCSSClass("th-value th-good");  PANEL_TH_DOT.setCSSClass("th-dot th-good");
} else {  PANEL_TH_BASE.setCSSClass("th-base th-bad");  PANEL_TH_VALUE.setCSSClass("th-value th-bad");  PANEL_TH_DOT.setCSSClass("th-dot th-bad");
}

Example


Thermometer with "good" visualization

th_good.PNG

Thermometer with "good" visualization

th-bad.PNG

Used CSS


.th-border-top {  border-top: 1px solid #cccccc;
}
.th-border-top-baseline {  border-top: 3px solid black;
}
.th-base {
}
.th-value {
}
.th-dot {  border-radius: 40px 40px 40px 40px / 40px 40px 40px 40px;  border: 1px solid #cccccc;
}
.th-bad {  background-color: red;
}
.th-good {  background-color: green;
}


Apllication as Example

Example Application can be downloaded from GitHub:

 

I hope you have fun with this component.

JavaScript Compression for Design Studio Performance

$
0
0

JavaScript compression is a code compression mechanism that allows us to compress and minify JavaScript files. In the programming world, this compression mechanism is called “Code Minification”. In JavaScript, it is achieved by removing extra white space, comments and unwanted characters. The size of the file will be reduced by 30%-80%. However, the functionality of the code remains unaltered despite aggressive compression.

In Design Studio, SAP supports JavaScript compression from version 1.3 SP01. With this new feature, we can achieve the following performance enhancements.

  • Quicker load time of dashboards due to compressed scripts
  • Reduced Bandwidth consumption of the server
  • Reduced HTTP traffic – Since the JS files are compressed and packed together, they are downloaded to client in minimal requests. This reduced traffic improves server response time.
  • Faster performance of the JS executor – Since unwanted white space, comments and characters are removed, the JS executor performs faster.


Steps to enable JavaScript compression
Step 1:
Stop the Apache Tomcat server. If Tomcat is deployed across multiple nodes, stop all Tomcat servers.

Step 2:
Open the “InstallPath\SAP BusinessObjects\tomcat\conf\server.xml” file and search for the connector port tag.

Step 3:
Under the connector port tag the following attributes are available – noCompressionUserAgents and compressableMimeType.

  • noCompressionUserAgents – Specifies the compression agents available in the system
  • compressableMimeType – Specifies the MIME types that can be used for compression

 


Step 4:
Add ‘application/javascript’ at the end of the compressableMimeType attribute values. This will enable JavaScript compression.


Step 5:
Apply these settings in all Tomcat servers. If deployed across multiple nodes, start all the Tomcat servers.

This compression would be help in a production environment where the application loading time is critical. Reduced size of the application would help in improved load time and server throughput.


Source : http://www.visualbis.com/blogs/design-studio/2014/09/10/javascript-compression-for-design-studio-performance/

How To Pass a Filter to Data Selection in Chart & 2nd Data Source (with modification on the way)

$
0
0

Prerequisetes

you have two data sources and a chart, you want to pass the filter to those and modify on the way..


Basics

the problem starts with conversion from external to internal key, this must be solved by yourself (unfortunatelly)


Working Example

20140911-174714_capture.gif

Simple Code as an Idea

 

// get the filter with external key
var externalFilter = DS_1.getFilterExt("0BC_PERS1");
// place an error message for debug
APPLICATION.createErrorMessage("Filter External: " + externalFilter);
// split the external filter
var splittedString = externalFilter.split(";");
// create as many variables as many you need in the array
var first = "";
var second = "";
// this array is requried for the "primitive" transformation between internal and external key
var anArrayOf5ForLoop = [0,1,2,3,4];
// loop on the splitted external keys
splittedString.forEach(function(filterELem, index) {  // clean up any spaces  filterELem = Convert.replaceAll(filterELem, " ", "");  // very basic conversion from external key to internal key, add 0 (works only for me!)  anArrayOf5ForLoop.forEach(function(loopElem, index) {   if(filterELem.length < 5) {    filterELem = "0" + filterELem;   }  });
// set the variables based on index   if (index == 0) {  first = filterELem;  }  if (index == 1) {  second = filterELem;  }
});
// create an array with all variables, seems empty values are not breaking selection
var thirdAsEmpty = "";
var selectionsAsArray = [first, second, thirdAsEmpty];
// just for debug
var selectionArrayAsString = "";
selectionsAsArray.forEach(function(selArrayInternalElem, index) {  selectionArrayAsString = selectionArrayAsString + " | " + selArrayInternalElem;
});
APPLICATION.createErrorMessage("Filter Internal: " + selectionArrayAsString);
// hmm, works only for me as I can convert
CHART_1.setDataSelection({  "0BC_PERS1": selectionsAsArray
});
DS_2.setFilterExt("0BC_PERS1", externalFilter);

Tricky Points

 

there are some tricky points in the script above.

 

1) by getFilterExt() you get "external" key, and selection on chart requires "internal" key. you have to conver, this can be done somehow in code - but most probably hard coded...

 

2) you need to pass an real ARRAY to setDataSelection - and this is a problem, as today you cannot create an array of "dynamic" size, but you can try to create an array of eg. 20 and leave the rest empty, seems this will work.

 

Source Code

A test source code can be taken from:

DesignStudioBiAppRepository/content.biapp at master · KarolKalisz/DesignStudioBiAppRepository · GitHub

 

make a try!

Karol's SDK Components

$
0
0

Following components are included in the SDK pack which is provided by me.

You can use them for free.


A component summary with links to all documents with details.

 

  • Application Header

          An interesting example how to create own content using Absolute Layout and some SAPUI5 componenents.

Design Studio SDK: Basic Business Card (a mix of self made SAP UI5 content)

 

  • Application Header

          Basic wrapper on SAP UI5 Application Header

          Design Studio SDK: Application Header Component

 

  • Color Picker

          Basic wrapper on SAP UI5 Color Picker

          Design Studio SDK: Color Picker Component

 

  • Fallback Picture

          Basically a picture with fallback in case server does not have the correct one.

          Design Studio SDK: Fallback Picture Component

 

  • Image Carousel

          Simple Selection Component (like a drop down) with Image in addition. It supports "selection" event, which can be used for some other actions.

          Design Studio SDK: Image Carousel Component


  • Link (1.0.5)

          Basic wrapper on SAP UI5 Link with "press" event

          Design Studio SDK: Link Component


  • Notification Bar

          Different style of positing notifications (messages) to end users. Reuse of the SAPUI5 proposed "NotificationBar" (demo link) as Design Studio component.

          Design Studio SDK: NotificationBar Component

 

  • Paginator

          Basic wrapper on SAP UI5 Paginator

          Design Studio SDK: Paginator Component

 

  • Progress Indicator

          Basic wrapper on SAP UI5 Progress Indicator

          Design Studio SDK: Progress Indicator Component

 

  • Rating Indicator

          Basic wrapper on SAP UI5 Rating Indicator

          Design Studio SDK: Rating Indicator Component

 

  • Simple Date Object [Non UI]

          Date-based navigation (roll days, months) in BIAL script directly, format days to own formats.

          Design Studio SDK: Image Carousel Component

 

  • Range Slider (1.0.5)

          Basic wrapper on SAP UI5 Range Slider (Slider with 2 values)

Design Studio SDK: Range Slider Component


  • Slider

          Basic wrapper on SAP UI5 Slider

          Design Studio SDK: Slider Component

 

Releases (link to always latest)

 

Prerequisites

     Those components require release 1.3 Support Package 1 (see Design Studio 1.3 Support Package 1 Released)

 

Installation

  • Open Design Studio
  • Download the .ZIP file
  • Go to menu "Tools"
  • Select "Install Extension to Design Studio"
  • Select "Archive..."
  • FInd the ZIP and start installation.


I hope you will have good use of those components.

Viewing all 662 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>