Custom Widget Guide
Custom Widget Guide
1 Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2 Web Components. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
3 Restrictions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
6 Custom Widgets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
6.1 Custom Widget JSON Reference. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .15
6.2 Web Component JavaScript. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .29
Using Script API Data Types in JavaScript Functions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Importing Script API Data Types with Type Libraries. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
Using Data Bindings. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .38
6.3 Styling Panel. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
6.4 Builder Panel. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
10 Enabling Linked Analysis for Your Custom Widget (Optimized Story Experience). . . . . . . . . . . 62
11 Configuring Bookmark Support for Your Custom Widget (Optimized Story Experience)
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
12 Best Practices. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
12.1 Dispatching a Property Change. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
12.2 Calling Methods of Passed Objects of Script API Types. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
The SAP Analytics Cloud Custom Widget framework lets you extend the predefined set of widgets provided by
analytics designer with your own custom widgets.
This is very useful, for example, if you need a specific user interface element, a particular visualization of data,
or a certain functionality in your analytic application that is not provided by the predefined set of widgets.
Custom widgets seamlessly integrate into SAP Analytics Cloud, analytics designer. Like any other predefined
widget, a custom widget provides the following capabilities:
• It is listed in the widget menu list from where you can add it to the canvas.
• It can be moved and resized on the canvas.
• It appears in the widget outline.
• It can contribute script methods to the analytics designer script language.
• It can provide areas in analytics designer where you can set property values of the custom widget at design
time (Styling Panel and Builder Panel).
Custom widgets are technically a simple set of files. The most basic custom widget consists of simply a JSON
file and a JavaScript file. More complex custom widgets add more files to this (JavaScript files, CSS files,
images, and so on).
To create custom widgets, you don’t need special software. At the beginning, a simple text editor will
do. However, when your custom widgets get more complex, a good JavaScript text editor will make your
development efforts easier.
The basic idea of Web Components is to provide custom HTML elements (so-called custom elements) in the
HTML DOM (Document Object Model) of a web page that do not interfere with the rest of the HTML DOM.
In fact, rendering and styling of custom elements is strictly isolated from the remaining HTML DOM. This is
achieved by separating a custom element’s HTML DOM from the HTML DOM of the web page in a shadow
DOM.
This document doesn’t assume any prior knowledge of Web Components. It explains the relevant ideas and
details of Web Components in the text, along with sample code.
For more information on Web Components, see the Mozilla MDN web docs on Web Components.
Note
Browser
Currently, custom widgets work in Google Chrome and Microsoft Edge (version 79 and higher) only.
Unsupported Features
Currently in both SAP Analytics Cloud, analytics designer and Optimized Story Experience, the following
features aren't supported for custom widgets:
Additionally in SAP Analytics Cloud, analytics designer, linked analysis and bookmark aren't supported.
Note
Now in Optimized Story Experience, linked analysis and bookmark are supported for custom widgets.
In specific cases, you need to do further actions to enable linked analysis. For more information, refer to
Enabling Linked Analysis for Your Custom Widget (Optimized Story Experience) [page 62].
To enable bookmark for your custom widget, you need to do further actions in its JSON file. For more
information, refer to Configuring Bookmark Support for Your Custom Widget (Optimized Story Experience)
[page 64].
Export
For a custom widget whose resource files are hosted by SAP Analytics Cloud, after transporting it or an
optimized story or analytic application containing it to another tenant, you need to upload the custom widget
there.
Hosting
It isn't yet supported to host the resource files on SAP Analytics Cloud tenants based on SAP Neo platform.
Data Binding
Note the following restrictions if you want to add data binding to your custom widget.
In both SAP Analytics Cloud, analytics designer and Optimized Story Experience:
• Custom widgets with data binding are only supported in optimized view mode.
• Filter lines won't apply to custom widgets either at design time or runtime.
• Custom widgets won't be affected by value selection changes in input controls at design time, while input
controls work on them at runtime.
From a hosting point of view, a custom widget consists of two types of files: the custom widget JSON file
(explained in more detail in a later section) and the resource files.
The custom widget JSON file contains the metadata of a custom widget. It defines all the ingredients of
a custom widget and references its resource files by their URLs. You need to upload the JSON file of your
custom widget to SAP Analytics Cloud. For more information, refer to Uploading a Custom Widget to Analytics
Designer [page 12].
The resource files are all the files of the custom widget that make it work properly, for example, JavaScript files,
CSS files, HTML files, image files, and so on.
You can upload and host the resource files of your custom widget in either your own web server or SAP
Analytics Cloud:
Note
• The SAP Analytics Cloud instance never connects to the Web server to request resource files. It is
always the browser that requests resource files.
• When you save an analytic application or optimized story on the SAP Analytics Cloud instance,
only a reference to the custom widget is saved with the analytic application or optimized story.
Specifically, it is a reference to the custom widget JSON of the custom widget on the SAP Analytics
Cloud instance. Analytic applications or optimized stories never store resource files of custom
widgets with them.
Sample Code
"webcomponents": [
{
"kind": "main",
"tag": "com-sap-sample-coloredbox",
"url": "/coloredbox_main.js",
"integrity": "",
"ignoreIntegrity": true
},
{
"kind": "styling",
"tag": "com-sap-sample-coloredbox-styling",
"url": "/coloredbox_styling.js",
"integrity": "",
"ignoreIntegrity": true
},
{
"kind": "builder",
"tag": "com-sap-sample-coloredbox-builder",
"url": "/coloredbox_builder.js",
"integrity": "",
"ignoreIntegrity": true
}
],
Remember
Keep the following in mind when you upload the resource files:
• Ensure that you have the Create permission for Custom Widget.
• The file name extension of the compressed file should be .zip.
• The ZIP file should be no larger than 5 MB.
• The ZIP file only supports the following resources files: Web Component JavaScript, Web
Component JavaScript of the Styling Panel, Web Component JavaScript of the Builder Panel, and
icon. CSS and HTML files are not supported.
Icon only supports PNG or JPG file.
• Subfolders in the ZIP file are not supported.
• At most 25 custom widgets with resources hosted by SAP are allowed on each tenant.
Restriction
It isn't yet supported to host the resource files on SAP Analytics Cloud tenants based on SAP Neo
platform.
To create your own custom widget, copy a simple sample custom widget, modify it, and add it to the analytics
designer.
Prerequisites
You need a web server that hosts the resources of your custom widget (JavaScript files, CSS files, images,
and so on) (see chapter Hosting Custom Widgets [page 8] for more information). In the samples below, let’s
assume that your custom widget resources are hosted on:
Example
https://www.sample.com/customwidgets
To create a simple sample custom widget as a starting point, create a folder coloredbox and place the
following files into it:
• coloredbox.json (find the source code in chapter Custom Widget JSON [page 42])
• coloredbox.js (find the source code in chapter Web Component JavaScript [page 46])
• coloredbox_styling.js (find the source code in chapter Web Component JavaScript of the Styling
Panel [page 50])
• coloredbox_builder.js (find the source code in chapter Web Component JavaScript of the Builder
Panel [page 54])
• icon.png (any 16 x 16 pixel icon will do)
Modify the sample Colored Box custom widget to the Box custom widget in the following steps:
Procedure
JSON Property Name Old JSON Property Value New JSON Property Value
id "com.sap.sample.coloredbo "com.sample.box"
x"
customElements.define("com-sap-sample- customElements.define("com-sample-box",
coloredbox", ColoredBox); Box);
customElements.define("com-sap-sample- customElements.define("com-sample-box-
coloredbox-styling", styling", BoxStylingPanel);
ColoredBoxStylingPanel);
customElements.define("com-sap-sample- customElements.define("com-sample-box-
coloredbox-builder", builder", BoxBuilderPanel);
ColoredBoxBuilderPanel);
9. On your web server, create a folder box which can be reached with this URL: https://www.sample.com/
customwidgets/box.
10. Upload the files box.js, box_styling.js, box_builder.js and icon.png to your web server to folder
https://www.sample.com/customwidgets/box.
Follow the steps to upload a custom widget to SAP Analytics Cloud, analytics designer so that you can use it as
other widgets in your analytic application.
Prerequisites
To create and upload custom widgets, the Create permission for Custom Widget must be selected in the role
that you are assigned.
Procedure
1. On the Analytic Applications start page, choose the Custom Widgets tab.
2. Select (Create).
3. In the Upload File dialog, choose Select File.
4. Select the custom widget file, for example box.json.
Results
When you want to insert the custom widget into your analytic application, you can find it via (Add)
Custom Widgets.
Next Steps
However, when you add a custom widget that differs in minor version from the one present in analytics
designer, it replaces the present one. For example, if a custom widget of version 1.5.0 is present in analytics
designer, then adding either version 1.4.0 or 1.6.0 replaces version 1.5.0.
To remove the custom widget from analytics designer, follow these steps:
Procedure
Web Component JavaScript of Styling Optional Implements the custom element of the
Panel Styling Panel of the custom widget
(Web Component)
Web Component JavaScript of Builder Optional Implements the custom element of the
Panel Builder Panel of the custom widget
(Web Component)
The following sections explain the valid properties of the custom widget JSON. Any other properties present in
a custom widget JSON render it invalid.
Root Object
The root object of the custom widget JSON specifies the custom widget. Its JSON properties are:
Tip
You can also use a Data-
URL. A Data-URL lets
you include a resource
directly into the custom
widget JSON that would
be otherwise requested
via the network.
• true
• false
Webcomponent Object
A Webcomponent object specifies one web component of a custom widget. Its JSON properties are:
Note
The unique name must
contain at least one hy-
phen (-) to differentiate
a custom element from
regular HTML elements.
Tip
Take the custom widget
ID and replace all dots (.)
with hyphens (-) to cre-
ate a unique name for
the custom element. For
example, turn the cus-
tom widget ID
"com.sap.sample.
coloredbox" into the
unique name "com-
sap-sample-
coloredbox"
Tip
Append the major ver-
sion number to the
tag name, for example,
"com-sap-sample-
coloredbox-1". This
avoids confusion be-
tween multiple versions
of a custom widget that
have been added side-
by-side to analytics de-
signer (see section Up-
loading a Custom Widget
to Analytics Designer
[page 12]).
Tip
To quickly obtain a
hash value add your
custom widget to ana-
lytics designer (setting
the JSON property
ignoreIntegrity to
false). This operation
fails, as expected, but
the browser console lists
the expected hash value.
• true
• false
A Properties object specifies the properties of a custom widget. Each JSON property name of a Properties
object is the name of a custom widget property. Its value is an object with the following JSON properties:
• "string"
• "number"
• "boolean"
• "integer"
• "string[]"
• "number[]"
• "boolean[]"
• "integer[]"
• You can also use certain
script API data types.
For more information
see section Using Script
API Data Types in Java-
Script Functions [page
37].
• You can also use simple
object types. For more
information see section
Simple Object Types.
Note
When you modify a property that is an array in a script method (see section Methods Object), always
reassign the modified array to the property after the modification operation.
For example, your custom widget has a property listOfItems of type string[].To add a new item to
this array in a script method, follow this pattern: Create a helper reference to the array, add the item, then
reassign the helper reference to the array property, like this:
Methods Object
A Methods object specifies the script method of a custom widget. Each JSON property name of a Methods
object is the name of a custom widget script method. Its value is an object with the following JSON properties:
Note
If you omit this property
then the Custom Widget
framework looks up and
calls a JavaScript func-
tion of this name (a so-
called native JavaScript
function) in the Web
Component JavaScript
file whose property
kind has the value
"main". If you change a
custom widget property
in a native JavaScript
function, dispatch a
"propertiesChang
ed" custom event to no-
tify the Custom Widget
framework of the
change. For a code ex-
ample, see the section
Implementing Property
Setter and Getter Meth-
ods Consistently [page
68]. Otherwise this may
lead to outdated prop-
erty values when you re-
trieve them in a script
method.
A Parameter object specifies one parameter of a script method of a custom widget. Its JSON properties are:
• "string"
• "number"
• "boolean"
• "integer"
• "string[]"
• "number[]"
• "boolean[]"
• "integer[]"
• You can also use certain
script API data types.
For more information
see chapter Using Script
API Data Types in Java-
Script Functions [page
37] .
• You can also use simple
object types. For more
information see section
Simple Object Types.
Events Object
An Events object specifies the events of a custom widget. Each JSON property name of an Events object is the
name of a custom widget event. Its value is an object with the following JSON properties:
A Types object specifies the custom types of a custom widget. Custom types can be either custom data
structures or custom enumerations. They can be used as types for properties, arguments and return types of
methods inside the custom widget JSON. In the analytic application or optimized story you can use custom
types for global variables, script object function arguments and return types.
Each JSON property name of a Types object is the name of a custom type. Its value defines the custom type
property, either a custom data structure or a custom enumeration.
To avoid clashes of custom type names, internally each custom type name is made unique by using a qualified
custom type name, which contains the custom widget ID. You don’t need to specify the qualified custom type
name in the types property section.
Note
When you’re using custom type names in the Properties, Methods, and Parameter objects, they have
precedence over built-in type names.
For specifying a custom data structure, you can use the following JSON properties:
For specifying a custom enumeration type, you can use the following JSON properties:
• "Enum"
A CustomTypeProperty object specifies one property of a custom type of a custom widget. Its JSON properties
are:
Note
Types that can be used
as argument types or as
object expressions are
identified in the analytics
designer API Reference
(search on the SAP Help
Portal for SAP Analytics
Cloud Analytics Designer
API Reference Guide).
A CustomEnumValue object specifies one value of a custom enumeration type of a custom widget. Its JSON
properties are:
DataBindings Object
You can specify the data binding of your custom widget in the dataBindings property. You can define only
one object, and only the first one will be used if you define multiple objects.
The keys of the dataBindings property represent the name of the object.
Also a default Builder panel will be available in design time if you define a DataBindings object in the JSON file.
If you've specified your own Builder panel via the Webcomponent object, this automatically generated panel
won't be used.
Feed Object
In addition to simple types (string, number, integer, boolean), script API data types, custom types and
arrays of these types, you can also specify a simple object type using the form Object<type>.
This type represents a JavaScript object with keys of type string and values of type<type>. For example, the
type Object<string> represents a JavaScript object with keys of type string and values of type string.
Supported type<type>:
The following example shows how to use an object of type Object<number>, which represents a JavaScript
object with keys of type string and values of type number:
Sample Code
{
...
"properties": {
"myNumbers": {
"type": "Object<number>",
"description": "A collection of number object"
}
},
"methods": {
"putNumber": {
"parameters": [
{"name": "name", "type": "string"},
{"name": "value", "type": "number"}
],
"description": "Adds a number, using a name, to the collection."
},
"getNumber": {
"parameters": [
{"name": "name", "type": "string"}
],
"returnType": "number",
"description": "Returns a number, using a name, from the
collection."
}
}
}
Sample Code
var number1 = 1;
var number2 = 2;
customWidget.put("n1", number1);
customWidget.put("n2", number2);
var n = customWidget.get("n2");
Each Web Component is implemented in a Web Component JavaScript file, which defines and registers a
custom element and implements the custom element’s JavaScript API.
For an example, see the section below that explains the Web Component JavaScript of the sample custom
widget Colored Box.
During the lifetime of a custom widget, the Custom Widget framework calls specific JavaScript functions of the
Web Component in a defined order. You can implement these JavaScript functions to control the behavior of
the custom widget. See the next section for a detailed explanation of each of these functions.
When the custom widget is rendered for the first time, the Custom Widget framework calls the following
sequence of JavaScript function on the custom widget:
1. constructor()
2. onCustomWidgetBeforeUpdate()
3. Property setter functions, if present, to update the custom widget properties
4. onCustomWidgetAfterUpdate()
5. connectedCallback()
When the custom widget is updated, the Custom Widget framework executes the following sequence of
JavaScript function calls on the custom widget:
1. onCustomWidgetBeforeUpdate()
2. Property setter functions, if present, to update the custom widget properties
3. onCustomWidgetAfterUpdate()
When the custom widget is removed from the canvas or the analytic application is closed, the Custom Widget
framework executes the following sequence of JavaScript function calls on the custom widget:
1. onCustomWidgetDestroy()
2. disconnectedCallback()
Function onCustomWidgetDestroy is not called on custom widgets when their visibility is set to false.
It is also not called when custom widgets are an invisible part of a container, for example, when in the
non-visible Tab of a TabStrip.
When the custom widget is resized on the canvas, the Custom Widget framework executes the following
JavaScript function call on the custom widget:
1. onCustomWidgetResize()
Note
At design time, when you are about to drag a custom widget on the canvas of analytics designer, the
custom widget is cloned to provide you an object to drag. Thus, the constructor of the custom widget is
called, as well as the connectedCallback and disconnectedCallback callbacks.
The following table lists the JavaScript API of a Web Component of a custom widget.
The argument
oChangedProperties is a Java-
Script object containing the changed
properties as key-value pairs. The key
is the name of the property, the value is
the changed value of the property.
Tip
For Web Components that imple-
ment the actual custom widget
(with a value of kind="main" in
the custom widget JSON), some
more build-in properties are passed
in oChangedProperties in the
very first call, in addition to the
properties defined in the custom
widget JSON:
Descrip-
Property Type tion
Descrip-
Property Type tion
Possible
values
are:ol
• tru
e
• fal
se
Possible
values
are:
• tru
e
• fal
se
The argument
oChangedProperties is a Java-
Script object containing the changed
properties as key-value pairs. The key
is the name of the property, the value is
the changed value of the property.
Tip
It’s good practice to implement
this function such that it may
be called by the framework at
any time, restoring the state of
the custom widget. For simple cus-
tom widgets without much state
data, it may be sufficient to recre-
ate the custom widget just from
scratch. See also the section on
disconnectedCallback()
below.
Tip
It’s good practice to implement
this function such that it may
be called by the framework at
any time, saving the state of
the custom widget – unless the
custom widget is removed perma-
nently. For simple custom widg-
ets without much state data, sav-
ing their state may not be nec-
essary. See also the section on
connectedCallback() above.
Note
This function is called only with
Web Components that implement a
custom widget proper (with a value
of kind="main" in the custom
widget JSON) but not with Styl-
ing Panels or Builder Panels (with
values of kind="styling" or
kind="builder").
Tip
If your custom widget doesn’t de-
pend on resizing, don’t implement
this function. This frees the frame-
work from calling this (potentially
empty) function repeatedly during
a resize operation.
Note
To improve the performance of a
resize operation, this function is
called in certain time intervals. The
length of those time intervals may
be subject to change.
You can implement property setter functions to set the properties of your custom widget.
A property setter function starts with the set keyword (object accessor notation) followed by the name of the
property. It takes the new property value as an argument. The following example shows a setter function for a
property color:
set color(newColor) {
this.style["background-color"] = newColor;
}
Property setters are optional. The property setters are called by the Custom Widget framework when
implemented and skipped when they are not.
Note
A getter function, implemented in a similar way using the get keyword, is ignored by the Custom Widget
framework.
Tip
Depending on the functional design of your custom widget, a common programming pattern is to omit the
implementation of specific property setter functions and instead implement the update of the associated
properties in the onCustomWidgetAfterUpdate function.
You can use certain script API data types, for example, Button, in native JavaScript functions in a Web
Component implementation of your custom widget. For example, you can use them as function arguments and
return values. You can also call functions provided by those script API data types.
A native JavaScript function is a JavaScript function in a Web Component of your custom widget as opposed to
a script method that your custom widget exposes in the analytics designer script editor.
The following rules apply when using script API data types in native JavaScript functions:
• You must call only documented functions provided by a script API data type.
• You must prepare your code to correctly accept the returned value of a function call on a script API
data type, as it is returned asynchronously. To retrieve such a function result correctly, apply one of the
following programming patterns in your native JavaScript function:
1. Use the await and async keywords. The following example shows how to call the function getText of
a Button that has been passed as a function argument to function myFunction:
Example
Sample Code
2. Use the then function. The following example shows how to call the function getText of a Button
that has been passed as a function argument to function myFunction:
Sample Code
3. If your browser doesn’t support the await and async keywords or the then function, use a transpiler
that provides the functionality of one of the above patterns.
Your custom widget can use script API data types in its implementation, for example, with method arguments,
properties, and return values.
Note
To indicate that your Web Component uses a specific script API data type, request the appropriate type library
in the Custom Widget JSON. For example, if your Web component uses a Button and a Table script API data
type, add this line to the Custom Widget JSON:
Sample Code
Note
The list of type libraries is available in the Analytics Designer API Reference on SAP Help Portal at http://
help.sap.com.
In the web component, you can use the data bindings that you've defined in the custom widget JSON file to
access data and call supported APIs.
You need to define the data binding in the custom widget JSON first. In the following example,
“myDataBinding” is the name, which you can change.
"dataBindings": {
"myDataBinding": {
"feeds": [
{
"id": "dimensions",
"description": "Dimensions",
"type": "dimension"
},
{
"id": "measures",
"description": "Measures",
"type": "mainStructureMember"
}
]
}
}
Accessing Data
Now you can access the data by the name of dataBindings that you’ve defined in custom widget JSON, for
example, "myDataBinding" in the example above.
this.myDataBinding
Sample Code
{
"data": [
// resultset rows
{
"dimensions_0": { "id": "[Product_3e315003an].
[Product_Catego_3o3x5e06y2].&[PC4]", "label": "Alcohol" },
"dimensions_1": { "id": "[Location_4nm2e04531].
[State_47acc246_4m5x6u3k6s].&[SA1]", "label": "California" },
"measures_0": { "raw": 30720120.73, "formatted": "30.72 Million",
"unit": "USD" }
}
],
"metadata": {
"feeds": {
"measures": { "values": [ "measures_0" ], "type":
"mainStructureMember" },
"dimensions": { "values": [ "dimensions_0", "dimensions_1" ],
"type": "dimension" }
},
"dimensions": {
"dimensions_0": { "id": "Product_3e315003an", "description":
"Product" },
"dimensions_1": { "id": "Location_4nm2e04531", "description":
"Location" }
You can write scripts for result set traversal like this:
Sample Code
You can use the function getDataBinding to get DataBinding object that has data driven APIs the custom
widget can execute.
Sample Code
To find a list of APIs that can be called by DataBinding object, refer to Analytics Designer API Reference. Search
for DataBinding there.
The Styling Panel of a custom widget is an area in analytics designer where you can set property values of the
custom widget at design time. This area is located in the Styling tab of the Designer panel of analytics designer
and has the title Custom Widget Additional Properties.
The Styling Panel shares the screen space of the Styling tab with other UI elements.
The Styling Panel is also implemented as a Web Component. Its implementation is contained in a separate Web
Component JavaScript file.
Note
At design time, the analytics designer creates for each custom widget a separate Styling Panel next to
the canvas. As the Styling Panel is implemented as a Web Component, the same lifecycle functions and
Note
At design time, when you delete a custom widget from the canvas, then the Styling Panel is destroyed and
its lifecycle functions and callbacks onCustomWidgetDestroy and disconnectedCallback are called.
The exact sequence is not defined. If you need to clean up the state of your Styling Panel, implement this in
onCustomWidgetDestroy.
The Builder Panel of a custom widget is an area in analytics designer where you can set property values of the
custom widget at design time. This area is located in the Builder tab of the Designer panel of analytics designer
and has the title Builder.
The Builder Panel fills the entire screen space of the Builder tab.
The Builder Panel is also implemented as a Web Component. Its implementation is contained in a separate Web
Component JavaScript file.
Note
At design time, the analytics designer creates for each custom widget a separate Builder Panel next to
the canvas. As the Builder Panel is implemented as a Web Component, the same lifecycle functions and
callbacks are called like those of the Web Component proper (with a value of kind="main" in the custom
widget JSON) – except for onCustomWidgetResize, which is never called.
Note
At design time, when you delete a custom widget from the canvas, then the Builder Panel is destroyed and
its lifecycle functions and callbacks onCustomWidgetDestroy and disconnectedCallback are called.
The exact sequence is not defined. If you need to clean up the state of your Builder Panel, implement this in
onCustomWidgetDestroy.
The following sections lead you through the code of a very simple custom widget, the Colored Box.
Basically, it is nothing more than the name implies: a red box with a black border. Yet, it touches all relevant
capabilities of custom widgets: It can be inserted on the canvas, it can be moved and resized, it adds script
methods to the script editor, and it adds a Styling Panel and a Builder Panel with which you can modify
properties of the Colored Box in the analytics designer at design time.
The Colored Box custom widget consists of three Web Components: the actual Colored Box, the Styling Panel,
and the Builder Panel of the Colored Box.
File Description
coloredbox.json Custom widget JSON of the Colored Box. Find the source
code in chapter Custom Widget JSON [page 42].
coloredbox.js Web Component JavaScript file of the Colored Box. Find the
source code in chapter Web Component JavaScript [page
46].
icon.png Icon of the Colored Box. Any 16 x 16 pixel icon will do.
The following code shows the custom widget JSON of the Colored Box (file coloredbox.json):
Sample Code
{
"id": "com.sap.sample.coloredbox",
"version": "1.0.0",
"name": "Colored Box",
"description": "A colored box",
"newInstancePrefix": "ColoredBox",
"icon": "https://www.sample.com/customwidgets/coloredbox/icon.png",
"vendor": "SAP",
"eula": "",
"license": "",
"webcomponents": [
{
"kind": "main",
Sample Code
"id": "com.sap.sample.coloredbox",
"version": "1.0.0",
"name": "Colored Box",
"description": "A colored box",
"newInstancePrefix": "ColoredBox",
"icon": "https://www.sample.com/customwidgets/coloredbox/icon.png",
"vendor": "SAP",
"eula": "",
"license": "",
The Colored Box custom widget has the unique id "com.sap.sample.coloredbox", version "1.0.0.", and
the name "Colored Box", which is displayed in the analytics designer. It has the description "A colored
box", which is used, for example, in tooltips. The prefix for new instances of Colored Box custom widgets in the
analytics designer is "ColoredBox". The icon for the Colored Box is provided by a URL. The vendor is "SAP".
The end-user license agreement and the license string are empty.
The Colored Box custom widget is composed of the following three Web Components:
Sample Code
"webcomponents": [
{
"kind": "main",
"tag": "com-sap-sample-coloredbox",
"url": "https://www.sample.com/customwidgets/coloredbox/
coloredbox.js",
"integrity": "",
"ignoreIntegrity": true
},
{
"kind": "styling",
"tag": "com-sap-sample-coloredbox-styling",
"url": "https://www.sample.com/customwidgets/coloredbox/
coloredbox_styling.js",
"integrity": "",
"ignoreIntegrity": true
},
{
"kind": "builder",
"tag": "com-sap-sample-coloredbox-builder",
"url": "https://www.sample.com/customwidgets/coloredbox/
coloredbox_builder.js",
"integrity": "",
"ignoreIntegrity": true
}
],
The first Web Component is the actual Colored Box as indicated by the kind of "main". The name of its custom
element is "com-sap-sample-coloredbox". A URL references this Web Component’s JavaScript file. A
SHA256-hash can be added to check the integrity of the referenced file. However, for development purposes,
this check is disabled by setting the ignoreIntegrity property to true.
The third Web Component is the Builder Panel of the Colored Box as indicated by the kind of "builder".
The name of its custom element is "com-sap-sample-coloredbox-builder". A URL references this Web
Component’s JavaScript file. A SHA256-hash can be added to check the integrity of the referenced file.
However, for development purposes, this check is also disabled by setting the ignoreIntegrity property to
true.
Next are the properties of the Colored Box custom widget: color, opacity, width, and height.
Sample Code
"properties": {
"color": {
"type": "string",
"description": "Background color",
"default": "red"
},
"opacity": {
"type": "number",
"description": "Opacity",
"default": 1
},
"width": {
"type": "integer",
"default": 100
},
"height": {
"type": "integer",
"default": 100
}
},
The property color represents the background color of the Colored Box. Its type is a string with a default value
of "red".
The property opacity represents how much the area behind the Colored Box is obscured by the Colored Box.
Its type is a number with a default value of 1 (completely block the area behind the Colored Box).
The properties width and height represent the initial width and height of the custom widget. They are both
of type integer with a default value of 100 pixels. The properties width and height are special properties to
the Custom Widget framework: Implemented setter functions for them will not be called.
Sample Code
"methods": {
"setColor": {
"description": "Sets the background color.",
"parameters": [
{
"name": "newColor",
"type": "string",
"description": "The new background color"
}
Function setColor takes one parameter, the new color. The body property contains the script code, which
sets the passed parameter newColor , a string, to the Colored Box’s color property.
This function definition lets you write script code in the analytics designer script editor like, for example,
ColoredBox_1.setColor("red");.
Function getColor takes no parameters and returns the color. The body property contains the script code,
which returns the value of the Colored Box’s color property as a value of type string.
This function definition lets you write script code in the analytics designer script editor like, for example, var
theColor = ColoredBox_1.getColor();.
To keep the sample short, similar script methods for the opacity property are not implemented.
Sample Code
"events": {
"onClick": {
"description": "Called when the user clicks the Colored Box."
}
}
The following listing shows the Web Component JavaScript of the Colored Box (file coloredbox.js).
Sample Code
(function() {
let template = document.createElement("template");
template.innerHTML = `
<style>
:host {
border-radius: 25px;
border-width: 4px;
border-color: black;
border-style: solid;
display: block;
}
</style>
The Web Component JavaScript defines a new custom element com-sap-sample-coloredbox and
associates it with a JavaScript API represented by the ColoredBox class in the following code:
Sample Code
(function() {
...
class ColoredBox extends HTMLElement {
...
}
customElements.define("com-sap-sample-coloredbox", ColoredBox);
})();
The entire JavaScript code of the file is nested into a self-executing JavaScript function. This isolates
the scope of its variables from the global scope. The JavaScript API of the new custom element is
implemented in the ColoredBox class, which extends the JavaScript API of the HTMLElement class. Calling
customElements.define() defines a new custom element named com-sap-sample-coloredbox and
associates it with the JavaScript API represented by the ColoredBox class. Note that com-sap-sample-
coloredbox is the same name as the property value of property name tag of the first item in the
webcomponents array inside the custom widget JSON.
Template elements are an important part of Web Components. This template HTML element is a template for
the “shadow DOM” HTML element that represents the HTML DOM of the Colored Box, which exists separately
from the HTML DOM of the web page. It is inserted only during rendering into the HTML DOM of the web page.
Thus, you can, for example, apply styles to a shadow DOM element without interfering with the HTML DOM of
the web page.
This template element is very simple. It declares a CSS style of several CSS properties together with the CSS
selector :host. This CSS style is assigned to the template element. The selector :host, part of the Web
Components standard, selects the root of the shadow DOM element, that is, the root of our template element.
The CSS property display controls the visualization of the custom HTML element, possible values are, for
example, block, flex, or inline-block.
The JavaScript API of the custom element is defined by the ColoredBox class, which extends the
HTMLElement class:
Sample Code
Sample Code
constructor() {
super();
let shadowRoot = this.attachShadow({mode: "open"});
shadowRoot.appendChild(template.content.cloneNode(true));
this.addEventListener("click", event => {
var event = new Event("onClick");
First, the super() function is called. Then, the shadow DOM root element is created. Next, a copy of the
template element is added as a child element to the shadow DOM root element. Finally, an event listener is
attached to the custom element, listening for click events. If one such event occurs, its handler function
creates a new onClick event and dispatches (“fires”) it. Note that the name of the event, onClick, must be
the same name as it is defined in the events section of the custom widget JSON. Note also, that this in the
code refers to the custom element.
Lastly, to make managing the properties of the Web Component easier, an empty _props object is initialized.
The Colored Box handles updates of its two properties color and opacity, defined in the
properties array of the custom widget JSON, by implementing the onCustomWidgetBeforeUpdate and
onCustomWidgetAfterUpdate functions.
Sample Code
onCustomWidgetBeforeUpdate(changedProperties) {
this._props = { ...this._props, ...changedProperties };
}
Sample Code
onCustomWidgetAfterUpdate(changedProperties) {
if ("color" in changedProperties) {
this.style["background-color"] = changedProperties["color"];
}
if ("opacity" in changedProperties) {
this.style["opacity"] = changedProperties["opacity"];
}
}
Tip
To print a property value to the browser console, for example, during debugging, use code in the following
form:
Sample Code
console.log(`${this._props[<propertyName>]}`);
Sample Code
onCustomWidgetBeforeUpdate(changedProperties) {
// ...
console.log(`${this._props["widgetName"]}`);
}
Another way to implement the update of the Colored Box properties color and opacity is to implement
property setter functions.
Note
This approach is useful only for simple custom widgets with very few properties that are independent from
each other. The more properties are added to the custom widget the more likely it is that (potentially
complex) dependencies between properties arise. Managing these dependencies by implementing a
onCustomWidgetAfterUpdate function is the preferred alternative.
The following code shows the implementation of the property setter functions:
Sample Code
set color(newColor) {
this.style["background-color"] = newColor;
}
set opacity(newColor) {
this.style["opacity"] = newColor;
}
Property setter functions have the name of the property as it is defined in the custom widget JSON. A setter
function is prefixed with the set keyword (object accessor notation).
The actual implementation is simple: For example, in the setter function of the color property, the passed
value of the new color is assigned to the CSS style property background-color of the custom element.
The following listing shows the JavaScript of the Styling Panel of the Colored Box (file
coloredbox_styling.js).
This Styling Panel lets you change the color of the Colored Box in analytics designer.
Sample Code
(function() {
let template = document.createElement("template");
template.innerHTML = `
<form id="form">
<fieldset>
<legend>Colored Box Properties</legend>
<table>
<tr>
<td>Color</td>
<td><input id="styling_color" type="text" size="40"
maxlength="40"></td>
</tr>
</table>
<input type="submit" style="display:none;">
</fieldset>
</form>
`;
class ColoredBoxStylingPanel extends HTMLElement {
constructor() {
super();
this._shadowRoot = this.attachShadow({mode: "open"});
this._shadowRoot.appendChild(template.content.cloneNode(true));
this._shadowRoot.getElementById("form").addEventListener("submit",
this._submit.bind(this));
}
_submit(e) {
e.preventDefault();
this.dispatchEvent(new CustomEvent("propertiesChanged", {
detail: {
properties: {
color: this.color
}
}
}));
}
set color(newColor) {
this._shadowRoot.getElementById("styling_color").value = newColor;
}
get color() {
return this._shadowRoot.getElementById("styling_color").value;
}
}
customElements.define("com-sap-sample-coloredbox-styling",
ColoredBoxStylingPanel);
Sample Code
The entire JavaScript code of the file is nested into a self-executing JavaScript function. This isolates the
scope of its variables from the global scope. The JavaScript API of the new custom element is implemented
in the ColoredBoxStylingPanel class, which extends the JavaScript API of the HTMLElement class. Calling
customElements.define() defines a new custom element named com-sap-sample-coloredbox-
styling and associates it with the JavaScript API represented by the ColoredBoxStylingPanel class.
Note
Note that com-sap-sample-coloredbox-styling is the same name as the property value of property
name tag of the second item in the webcomponents array inside the custom widget JSON.
Sample Code
This template HTML element is a template for the shadow DOM HTML element that represents the HTML DOM
of the Styling Panel of the Colored Box.
This template element defines a simple form with id "form". It contains a table with an input field with id
"styling_color" and a length and capacity of 40 characters.
Tip
Defining a hidden input field of type submit is a trick to make to form work when the form contains more
than one input element. Strictly speaking, this it is not necessary yet, but as soon as you add more input
elements.
The JavaScript API of the custom element is defined by the ColoredBoxStylingPanel class, which extends
the HTMLElement class:
Sample Code
Sample Code
constructor() {
super();
this._shadowRoot = this.attachShadow({mode: "open"});
this._shadowRoot.appendChild(template.content.cloneNode(true));
this._shadowRoot.getElementById("form").addEventListener("submit",
this._submit.bind(this));
}
First, the super() function is called. Then, the shadow DOM root element is created. A reference to it is stored
in the local variable _shadowRoot, because the shadow DOM root element is referenced later in the color
setter and getter functions of this class. Next, a copy of the template element is added as a child element to
the shadow DOM root element. Finally, an event listener is attached to form, listening for submit events. If
one such event occurs, the event handler function _submit() is called. Calling bind() and passing this to
_submit() ensures that in _submit() the keyword this references the custom element.
Sample Code
_submit(e) {
e.preventDefault();
this.dispatchEvent(new CustomEvent("propertiesChanged", {
detail: {
properties: {
color: this.color
}
}
}));
}
The _submit() function calls function preventDefault() on the passed event object, which prevents
submitting the form to the server.
Then, a custom event is created. Its name "propertiesChanged" indicates a change of properties to the
Custom Widget framework (incidentally, you can also create and dispatch such events in the Web Component
The following code shows the implementation of color setter and getter functions.
Note
Do not confuse them with the color setter function of the ColoredBox class.
Sample Code
set color(newColor) {
this._shadowRoot.getElementById("styling_color").value = newColor;
}
get color() {
return this._shadowRoot.getElementById("styling_color").value;
}
The color setter function places a text representation of the new color into the input field of the Colored Box’s
Styling Panel. Incidentally, in this JavaScript code this function is not called directly, but by the Custom Widget
framework when the Styling Panel Sheet of the Colored Box is created, assigning an initial value to the input
field.
The color getter function returns the text of the input field of the Colored Box’s Styling Panel. This function is
called indirectly in the _submit() event handler function during the construction of the JSON event payload
when the current color is retrieved by this.color.
The following listing shows the JavaScript of the Builder Panel of the Colored Box (file
coloredbox_builder.js).
This Builder Panel lets you change the opacity of the Colored Box in analytics designer.
The JavaScript code is very similar to the Web Component JavaScript of the Styling Panel of the Colored Box.
Sample Code
(function() {
let template = document.createElement("template");
template.innerHTML = `
<form id="form">
<fieldset>
<legend>Colored Box Properties</legend>
<table>
this._shadowRoot.getElementById("form").addEventListener("submit",
this._submit.bind(this));
}
_submit(e) {
e.preventDefault();
this.dispatchEvent(new CustomEvent("propertiesChanged", {
detail: {
properties: {
opacity: this.opacity
}
}
}));
}
set opacity(newOpacity) {
this._shadowRoot.getElementById("builder_opacity").value =
newOpacity;
}
get opacity() {
return this._shadowRoot.getElementById("builder_opacity").value;
}
}
customElements.define("com-sap-sample-coloredbox-builder",
ColoredBoxBuilderPanel);
})();
Sample Code
(function() {
...
class ColoredBoxBuilderPanel extends HTMLElement {
...
}
The entire JavaScript code of the file is nested into a self-executing JavaScript function. This isolates the
scope of its variables from the global scope. The JavaScript API of the new custom element is implemented
in the ColoredBoxBuilderPanel class, which extends the JavaScript API of the HTMLElement class. Calling
customElements.define() defines a new custom element named com-sap-sample-coloredbox-
builder and associates it with the JavaScript API represented by the ColoredBoxBuilderPanel class.
Note
Note that com-sap-sample-coloredbox-builder is the same name as the property value of property
name tag of the third item in the webcomponents array inside the custom widget JSON.
Sample Code
This template HTML element is a template for the shadow DOM HTML element that represents the HTML DOM
of the Styling Panel of the Colored Box.
This template element defines a simple form with id "form". It contains a table with an input field with id
"builder_opacity" and a length and capacity of 5 characters.
Tip
Defining the hidden input field of type submit is a trick to make to form work when the form contains more
than one input element. Strictly speaking, this it is not necessary yet, but as soon as you add more input
elements.
The JavaScript API of the custom element is defined by the ColoredBoxBuilderPanel class, which extends
the HTMLElement class:
Sample Code
Sample Code
constructor() {
super();
this._shadowRoot = this.attachShadow({mode: "open"});
this._shadowRoot.appendChild(template.content.cloneNode(true));
this._shadowRoot.getElementById("form").addEventListener("submit",
this._submit.bind(this));
First, the super() function is called. Then, the shadow DOM root element is created. A reference to it is stored
in the local variable _shadowRoot, because the shadow DOM root element is referenced later in the color
setter and getter functions of this class. Next, a copy of the template element is added as a child element to
the shadow DOM root element. Finally, an event listener is attached to form, listening for submit events. If
one such event occurs, the event handler function _submit() is called. Calling bind() and passing this to
_submit() ensures that in _submit() the keyword this references the custom element.
Sample Code
_submit(e) {
e.preventDefault();
this.dispatchEvent(new CustomEvent("propertiesChanged", {
detail: {
properties: {
opacity: this.opacity
}
}
}));
The _submit() function calls function preventDefault() on the passed event object, which prevents
submitting the form to the server.
Then, a custom event is created. Its name "propertiesChanged" indicates a change of properties to the
Custom Widget framework. This custom event contains a JSON payload, which defines which properties
actually have changed and to what values. In this case it is the opacity property of the custom widget. Its
new value is retrieved by executing this.opacity, which implicitly calls the opacity getter function of class
ColoredBoxBuilderPanel.
The following code shows the implementation of the opacity setter and getter functions.
Sample Code
set opacity(newOpacity) {
this._shadowRoot.getElementById("builder_opacity").value = newOpacity;
}
get opacity() {
return this._shadowRoot.getElementById("builder_opacity").value;
}
The opacity setter function places a text representation of the new opacity into the input field of the Colored
Box’s Builder Panel. Incidentally, in this JavaScript code this function is not called directly, but by the Custom
Widget framework when the Builder Panel of the Colored Box is created, assigning an initial value to the input
field.
The opacity getter function returns the text of the input field of the Colored Box’s Builder Panel. This function
is called indirectly in the _submit() event handler function during the construction of the JSON event
payload when the current opacity is retrieved by this.opacity.
To enable a custom widget to run on a mobile device, set its supportsMobile property to true (see section
Root Object in chapter Custom Widget JSON Reference [page 15] for more information).
Caution
Before enabling this flag, please make sure that your custom widget’s functionality is working properly on
mobile devices.
When you develop a custom widget for a mobile device instead of a non-mobile device (for example, a desktop
or laptop computer), consider the following suggestions:
Sample Code
As a custom widget developer, you need to be aware of the prerequisites so that end users can export the
custom widgets to PDF at runtime.
Custom widgets support PDF export now. However, they might not be fully exported to PDF. To make sure that
all the defined elements can be exported to PDF:
Note
Due to the restrictions of JavaScript library and JavaScript PDF generator library that custom widgets
are based on, custom widgets might not be fully exported to PDF. For instance, the chart axes of a
custom widget based on D3.js won’t be exported. Custom widgets with CSS settings might not keep all
the styling when exported to PDF, either.
Learn how to enable linked analysis for your custom widget in certain cases.
Note
Only custom widgets with data binding are supported for linked analysis.
Custom widget as the target widget in linked analysis Linked analysis is enabled by default. As a custom widget
developer, you don't need to do any further actions.
Custom widget as the source widget in linked analysis, with-
out Filter on Data Point Selection
Custom widget as the source widget in linked analysis, with 1. In the contribution JSON file, set
Filter on Data Point Selection
supportsLinkedAnalysisFilterOnSelecti
on to true.
2. In Web Component JavaScript file, use Data Binding's
linked analysis APIs. For more information, refer to the
following section.
Remember
In addition, in Optimized Design Experience, story de-
signers need to select Filter on Data Point Selection
when configuring linked analysis.
Script APIs
You can use the following Data Binding's linked analysis APIs to set, remove the linked analysis filter and check
whether filter on data point selection has been enabled for the custom widget respectively:
dataBinding.getLinkedAnalysis().setFilters()
dataBinding.getLinkedAnalysis().removeFilters()
dataBinding.getLinkedAnalysis().isDataPointSelectionEnabled()
Here're script examples to set linked analysis based on data point selection:
Sample Code
const selection = {
"Location_4nm2e04531": "[Location_4nm2e04531].
[State_47acc246_4m5x6u3k6s].&[SA2]",
"Product_3e315003an": "[Product_3e315003an].
[Product_Catego_3o3x5e06y2].&[PC2]"
Sample Code
const selections = [{
"Location_4nm2e04531": "[Location_4nm2e04531].
[State_47acc246_4m5x6u3k6s].&[SA1]",
"Product_3e315003an": "[Product_3e315003an].
[Product_Catego_3o3x5e06y2].&[PC4]"
},
{
"Location_4nm2e04531": "[Location_4nm2e04531].
[State_47acc246_4m5x6u3k6s].&[SA2]",
"Product_3e315003an": "[Product_3e315003an].
[Product_Catego_3o3x5e06y2].&[PC2]"
}];
this.dataBindings.getDataBinding().getLinkedAnalysis().setFilters(selections);
Sample Code
this.dataBindings.getDataBinding().getLinkedAnalysis().removeFilters();
In optimized story experience, story viewers can save bookmarks with custom widgets. For more information
about bookmarks, refer to Bookmark and Share Story Views.
When the bookmark is opened, the lifecycle method onCustomWidgetAfterUpdate of the custom widget is
run. As a custom widget developer, you can get the property values and data bindings result in the bookmark.
Note
In SAP Analytics Cloud, analytics designer, bookmarking with custom widgets isn't supported.
To enable bookmark support for your custom widget, as a custom widget developer, you need to set
supportsBookmark to true in its JSON file. Optionally, you can exclude the properties from bookmarks.
In the custom widget JSON file, specify whether the custom widget support bookmarking by setting the
supportsBookmark to true or false, as supportsMobile.
Sample Code
"supportsBookmark": true
All properties are included in bookmarks by default. In the custom widget JSON file, you can exclude properties
from bookmarks by setting includeInBookmarks to false for them.
Sample Code
{
...
"properties": {
"color": {
"type": "string",
"description": "Background color",
"default": "red"
},
"opacity": {
Note
Some built-in properties, such as "width" and "height", are bookmarked by default, so any change to
their includeInBookmarks attribute is disregarded.
When updating the version of your custom widget, be mindful of potential impacts on the existing bookmarks
that contain prior versions. You must ensure the changes in custom widgets of new version are correctly
handled, which includes, but not limited to, the following cases:
• If a property is marked as excluded from bookmarks in the JSON file, bookmarks won't include it when
opened. You should revisit the Web Component JavaScript to verify its functionality.
• If a property is deleted in the new version, bookmarks created before the deletion still include it when
reopened. You need to decide whether to keep it in the Web Component JavaScript.
• If a property is changed in the new version, such as its data type, and there're already bookmarks that
contain it, you're recommended to reexamine the Web Component JavaScript to ensure compatibility.
• If a property was included in bookmarks but is marked as excluded from bookmarks in the new version's
JSON file, bookmarks created before still contain it when reopened.
In such cases, it's important for you to review the Web Component JavaScript to ensure your custom widget
aligns with these changes and continues to function as expected.
Note
If a custom widget's major version is changed, it's considered as a distinct widget. Therefore, the version
change doesn't affect the existing bookmarks.
When one of your properties has changed, store the changed property value, for example, in a variable of your
Web Component JavaScript, then notify the framework of this change by dispatching a propertiesChanged
custom event.
Sample Code
this.dispatchEvent(new CustomEvent("propertiesChanged", {
detail: {
properties: {
color: this.color
}
}
}));
Do not dispatch a propertiesChanged custom event in the following situations and functions, as this may
cause, for example, an endless loop in JavaScript execution:
• constructor
• onCustomWidgetBeforeUpdate
• onCustomWidgetAfterUpdate
• onCustomWidgetDestroy
• connectedCallback
• disconnectedCallback
Note that after dispatching the propertiesChanged custom event, the changed property value is not part
of the properties that are passed later to the onCustomWidgetBeforeUpdate(oChangedProperties) and
onCustomWidgetAfterUpdate(oChangedProperties) functions of this Web Component.
This is simply because, as this Web Component initiated dispatching the property change in the first place,
it doesn’t need to be notified of it again. However, the changed property is automatically passed to related
Web Components, because they need to be notified of the property change. Related Web Components of the
custom widget’s Web Component proper are the Web Component of the Styling Panel and the Web Component
of the Builder Panel. The same rules, with appropriately reversed roles, apply when dispatching property
changes in the Web Component of the Styling Panel and the Web Component of the Builder Panel.
Call methods of passed objects of script API data types, for example Button, only in script methods.
As you recall, there are two kinds of script methods, depending on where the script method is implemented:
1. The method definition in the custom widget JSON has a body property that contains the script method
implementation.
2. The method definition in the custom widget JSON does not have a body property. The script method
implementation is a native JavaScript function in the Web Component JavaScript.
The following example shows parts of a custom widget JSON and the corresponding Web Component
JavaScript.
This shows the correct use: The script method setButtonText1, with a body property, calls the setText
method of the passed Button object (see (1)), as does the script method setButtonText2, which has no
body property but is implemented as a native JavaScript function in the Web Component JavaScript (see (2)).
Don’t call methods of passed objects of script API data types outside these two situations, for example:
• in a constructor
• during rendering
• in other functions (see (3) and (4))
Sample Code
{
...
"methods": {
"setButtonText1": {
"parameters": [
{"name": "button", "type": "Button"},
{"name": "text", "type": "string"}
],
...
"body": "button.setText(text);" // (1) OK
},
"setButtonText2": {
"parameters": [
{"name": "button", "type": "Button"},
{"name": "text", "type": "string"}
],
...
},
...
},
...
}
Sample Code
To implement a pair of property setters and getters, for example setText and getText, which can be used like
in the following example:
Sample Code
customWidget.setText("Hello");
var text = customWidget.getText();
In the first style, you define the necessary elements (the property and the setter and the getter method) in the
custom widget JSON. Both methods are implemented in their body properties:
Sample Code
{
...
"properties": {
"text": {"type": "string"}
},
"methods": {
"setText": {
"parameters": [{"name": "newText", "type": "string"}],
"body": "this.text = newText;"
},
"getText": {
"returnType": "string",
"body": "return this.text;"
}
}
}
Written this way, the framework keeps track of the state of text. When the setter method is called, the
framework implicitly triggers a state change.
In the second style, you define the necessary elements (the property and the setter and getter method) in
the custom widget JSON and implement them in the Web Component JavaScript. Both methods are defined
Sample Code
{
...
"properties": {
"text": {"type": "string"}
},
"methods": {
"setText": {
"parameters": [{"name": "newText", "type": "string"}],
},
"getText": {
"returnType": "string"
}
}
}
Sample Code
Written this way, the framework keeps track of the state of text. When the setter method is called, the Web
Component JavaScript explicitly triggers a state change.
Caution
Do not mix those implementation styles when implementing property setters and getters!
For example, do not implement the setter of a property in the first style and the getter of the same property in
the second style. Strange effects may occur. For example, when running the following sample script with such
an implementation
Sample Code
customWidget.setText("Hello");
var text = customWidget.getText();
Note
In the Custom Widget framework there is a separation of concerns about custom widgets: The framework
handles the property state and the Web Component handles the rendering. For performance reasons,
all property changes are collected by the framework first (this includes the property changes during the
execution of a script) and only after that, rendering is executed. When mixing the above implementation
styles of setter and getter methods, the separation between property state handling and rendering
becomes fuzzy. This can lead to unexpected, for example, outdated, property values.
Simple types (string, integer, number, boolean) and script API data types (for example, Button) that are
passed to script methods are always returned by value, that is, a copy of the argument is returned.
Arrays and simple typed objects (for example, object<string>) that are passed to script methods are
returned either by reference or by value. It depends on where the script method’s implementation is defined:
• If the script method’s implementation is in the body property of the custom widget JSON, then arguments
of arrays and simple typed objects are returned by reference (the identical argument is returned).
• If the script method’s implementation is in a native JavaScript implementation of the Web Component
JavaScript, then arguments of arrays and simple typed objects are returned by value (a copy of the
argument is returned).
In the following example, an array that is passed to two script methods is returned either by reference (the
returned array is the identical array that was passed into the method) or returned by value (the returned array
is a copy of the array that was passed into the method):
Sample Code
{
...
"methods": {
"getArrayByRef": {
"parameters": [{"name": "values", "type": "integer[]"}],
"returnType": "integer[]",
"body": "return values;"
},
"getArrayByValue": {
"parameters": [{"name": "values", "type": "integer[]"}],
"returnType": "integer[]"
}
}
Sample Code
Sample Code
This section discusses a sample of a custom type definition (for more information on custom types, see
section Types Object in chapter Custom Widget JSON Reference [page 15]).
Sample Code
"types": {
"Document": {
"description": "A document",
"properties": [
{
"name": "name",
"type": "string",
"description": "Name of the document",
"mandatory": true
}
]
},
"Book": {
"extends": "Document",
"description": "A book document",
"properties": [
{
"name": "bookContentType",
"type": "BookContentType",
"description": "Content type of the book",
"mandatory": true
},
{
"name": "numberOfPages",
"type": "integer"
"description": "Number of pages",
}
]
},
"Video": {
"extends": "Document",
"description": "A video document",
Note
• The custom type Document (a custom data structure) has a mandatory property name of type
string.
• The custom types Book and Video (both custom data structures) extend the type Document. Thus,
they both have a property name of type string that they receive from the type Document.
• The custom type Book has a mandatory bookContentType property, which can contain a value of the
custom enumeration BookContentType.
• The custom type Book has an optional numberofPages property of type integer.
• The custom type Video has a mandatory videoContentType property, which can contain a value of
the custom enumeration VideoContentType.
• The custom type Video has an optional length property of type integer.
Hyperlinks
Some links are classified by an icon and/or a mouseover text. These links provide additional information.
About the icons:
• Links with the icon : You are entering a Web site that is not hosted by SAP. By using such links, you agree (unless expressly stated otherwise in your
agreements with SAP) to this:
• The content of the linked-to site is not SAP documentation. You may not infer any product claims against SAP based on this information.
• SAP does not agree or disagree with the content on the linked-to site, nor does SAP warrant the availability and correctness. SAP shall not be liable for any
damages caused by the use of such content unless damages have been caused by SAP's gross negligence or willful misconduct.
• Links with the icon : You are leaving the documentation for that particular SAP product or service and are entering an SAP-hosted Web site. By using
such links, you agree that (unless expressly stated otherwise in your agreements with SAP) you may not infer any product claims against SAP based on this
information.
Example Code
Any software coding and/or code snippets are examples. They are not for productive use. The example code is only intended to better explain and visualize the syntax
and phrasing rules. SAP does not warrant the correctness and completeness of the example code. SAP shall not be liable for errors or damages caused by the use of
example code unless damages have been caused by SAP's gross negligence or willful misconduct.
Bias-Free Language
SAP supports a culture of diversity and inclusion. Whenever possible, we use unbiased language in our documentation to refer to people of all cultures, ethnicities,
genders, and abilities.
SAP and other SAP products and services mentioned herein as well as
their respective logos are trademarks or registered trademarks of SAP
SE (or an SAP affiliate company) in Germany and other countries. All
other product and service names mentioned are the trademarks of their
respective companies.