Skip to Content
Technical Articles
Author's profile photo Ferry Djaja

Build a Custom Widget in SAP Analytics Cloud, Analytics Application

In this tutorial, we will build a simple custom widget in SAP Analytics Cloud, Analytics Application to show the gauge chart as shown below.

 

A custom widgets are implemented as Web Components. The Web Components are made of HTML, CSS and JavaScript.

Custom Widgets

Custom widget consists of the following files:

  • Custom Widget JSON
    The custom widget JSON file specifies the custom widget properties like id, version, name, description and so on.
  • Web Component JavaScript
    A custom widget is composed of one or more Web Components.
    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. It has the lifecyles: constructor(), onCustomWidgetBeforeUpdate(), onCustomWidgetAfterUpdate(), connectedCallback().
  • Web Component JavaScript of Styling Panel (optional)
    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. It is implemented as a Web Component.
  • Web Component JavaScript of Builder Panel (optional)
    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. It is implemented as a Web Component.
  • Icon file
    Any icon file image in 16×16 pixels.

Prerequisites

You need a web server that hosts the resources of the custom widget files (JavaScript files, CSS files, images, and so on).  Assume that your web server address is :

https://example.demo/customwidgets

Create a Custom Widget

The Gauge Box custom widget consist of three Web Components: the actual Gauge Box, the Styling Panel and the Builder Panel of the Gauge Box and it consist the following files:

  • box.json
    Custom Widget JSON of Gauge Box.
  • box.js
    Web Component JavaScript file of the Gauge Box.
  • box_sps.js
    Web Component JavaScript file of the Styling Panel of the Gauge Box.
  • box_bps.js
    Web Component JavaScript file of the Builder Panel of the Gauge Box.
  • icon.png
    Icon of the Gauge Box in any 16×16 pixel icon.

1. Custom Widget JSON of Gauge Box (box.json)

The Gauge Box custom widget has the unique ID, version, and the name, which is displayed in the analytics designer, Styling Panel.

The Gauge Box custom widget is composed of the following three Web Components:

The first Web Component is the actual Gauge Box as indicated by the kind of “main”. The second Web Component is the Styling Panel of the Gauge Box as indicated by the kind of “styling”. The third Web Component is the Builder Panel of the Gauge Box as indicated by the kind of “builder”.

Moving on, these are the properties of the Gauge Box custom widget: value, info, color, width and height.

The property value represents the value in percentage of the Gauge Box. The property info represents the title of the Gauge Box. The property color represents the color of the Gauge Box. And the properties width and height represent the initial width and height of the custom widget.

And then the script methods of the Gauge Box are defined:

The function setValue takes three parameters, newValuenewInfo and newColor. The body property contains the script code which sets the passed all the parameters to the respective Gauge Box’s properties.

Function getValue takes no parameter and returns the percentage value of the Gauge Box.

Finally, an onClick event is defined:

Note that the event has no parameters.

2. Web Components JavaScript (box.js)

This section shows the Web Component JavaScript of the Gauge Box (box.js).

(function() { 
	let template = document.createElement("template");
	template.innerHTML = `
		<style>
		:host {
			border-radius: 10px;
			border-width: 2px;
			border-color: black;
			border-style: solid;
			display: block;
		} 

		body {
		  background: #fff;
		}
		
		.metric {
		  padding: 10%;
		}
		
		.metric svg {
		  max-width: 100%;
		}
		
		.metric path {
		  stroke-width: 75;
		  stroke: #ecf0f1;
		  fill: none;
		}
		
		.metric text {
		  font-family: "Lato", "Helvetica Neue", Helvetica, Arial, sans-serif;
		}
		
		.metric.participation path.data-arc {
		  stroke: #27ae60;
		}
		
		.metric.participation text {
		  fill: #27ae60;
		}		
		</style>
		
		<div class="container">
		  <div class="row">
		    <div class="col-md-4 col-sm-4">
		      <div class="metric participation" data-ratio=".95">
		        <svg viewBox="0 0 1000 500">
			        <path d="M 950 500 A 450 450 0 0 0 50 500"></path>
					<text class='percentage' text-anchor="middle" alignment-baseline="middle" x="500" y="300" font-size="140" font-weight="bold">0%</text>
					<text class='title' text-anchor="middle" alignment-baseline="middle" x="500" y="450" font-size="90" font-weight="normal"></text>
  	            </svg>
		      </div>
		    </div>
		  </div>
		</div>
	`;

	class Box extends HTMLElement {
		constructor() {
			super(); 
			let shadowRoot = this.attachShadow({mode: "open"});
			shadowRoot.appendChild(template.content.cloneNode(true));
			
			this.$style = shadowRoot.querySelector('style');			
			this.$svg = shadowRoot.querySelector('svg');
			
			this.addEventListener("click", event => {
				var event = new Event("onClick");
				this.dispatchEvent(event);
			});
			
			this._props = {};
		}
		
		render(val, info, color) {
			var val1 = val * 0.01;
			var x = this.svg_circle_arc_path(500, 500, 450, -90, val1 * 180.0 - 90);
			var rounded = Math.round( val * 10 ) / 10;

			
			if(rounded >=0 && rounded <=100) {
				this.$style.innerHTML = ':host {border-radius: 10px;border-width: 2px;border-color: black;border-style: solid;display: block;}.body {background: #fff;}.metric {padding: 10%;}.metric svg {max-width: 100%;}.metric path {stroke-width: 75;stroke: #ecf0f1;fill: none;}.metric text {font-family: "Lato", "Helvetica Neue", Helvetica, Arial, sans-serif;}.metric.participation path.data-arc {stroke: ' + color + ';}.metric.participation text {fill: ' + color + ';}';
				this.$svg.innerHTML = '<path d="M 950 500 A 450 450 0 0 0 50 500"></path><text class="percentage" text-anchor="middle" alignment-baseline="middle" x="500" y="300" font-size="140" font-weight="bold">' + rounded + '%</text><text class="title" text-anchor="middle" alignment-baseline="middle" x="500" y="450" font-size="90" font-weight="normal">' + info + '</text><path d="' + x + '" class="data-arc"></path>"';
			}
		}
		  
		polar_to_cartesian(cx, cy, radius, angle) {
		    var radians;
		    radians = (angle - 90) * Math.PI / 180.0;
		    return [Math.round((cx + radius * Math.cos(radians)) * 100) / 100, Math.round((cy + radius * Math.sin(radians)) * 100) / 100];
		}
		
		svg_circle_arc_path(x, y, radius, start_angle, end_angle) {
		    var end_xy, start_xy;
		    start_xy = this.polar_to_cartesian(x, y, radius, end_angle);
		    end_xy = this.polar_to_cartesian(x, y, radius, start_angle);
		    return "M " + start_xy[0] + " " + start_xy[1] + " A " + radius + " " + radius + " 0 0 0 " + end_xy[0] + " " + end_xy[1];
		  };
		  

		onCustomWidgetBeforeUpdate(changedProperties) {
			this._props = { ...this._props, ...changedProperties };
		}

		onCustomWidgetAfterUpdate(changedProperties) {
			if ("value" in changedProperties) {
				this.$value = changedProperties["value"];
			}
			
			if ("info" in changedProperties) {
				this.$info = changedProperties["info"];
			}
			
			if ("color" in changedProperties) {
				this.$color = changedProperties["color"];
			}
			
			this.render(this.$value, this.$info, this.$color);
		}
	}	
	customElements.define("com-demo-gauge", Box);
})();

2.1 Template Object

The following code creates a template HTML element:

The template element represents the gauge chart.

2.2 JavaSript API of the Custom Element

  • Constructor
    The first function in the JavaScript API is the constructor.

    The super() function is called, then the shadow DOM root element is created. The copy of the template element is added as a child element to the shadow DOM root element. An element style and svg is selected by using querySelector where shadowRoot is a reference to the document fragment. Finally, an event listener is attached to the custom element, listening for click events. Lastly, to make managing the properties of the Web Component easier, an empty _props object is initialized.
  • Handling Custom Widget Updates
    In the onCustomWidgetBeforeUpdate() function, the properties in the changedProperties are merged with the properties of the _props object. The _props contains the state of all Gauge Box properties before the Gauge Box is updated.

    In the onCustomWidgetAfterUpdate() function, the properties in the passed changedProperties object is used to directly set the gauge value, info(text information) and color of Gauge Box.

    And finally call the render() function to update the chart.

3. Web Components JavaScript of the Styling Panel (box_sps.js)

The Styling Panel lets you change the background color of the Gauge chart in analytics designer.

This following code shows the Web Component JavaScript of the Styling Panel (box_sps.js).

(function()  {
	let template = document.createElement("template");
	template.innerHTML = `
		<form id="form">
			<fieldset>
				<legend>Color Properties</legend>
				<table>
					<tr>
						<td>Color</td>
						<td><input id="sps_color" type="text" size="40" maxlength="40"></td>
					</tr>
				</table>
				<input type="submit" style="display:none;">
			</fieldset>
		</form>
	`;

	class BoxSps 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("sps_color").value = newColor;
		}

		get color() {
			return this._shadowRoot.getElementById("sps_color").value;
		}
	}

customElements.define("com-demo-box-sps", BoxSps);

The Web Component JavaScript defines a new custom element com-demo-box-sps. The JavaScript API of the new custom element is implemented in the BoxSps class which extends the JavaScript API of the HTMLElement class.

3.1 Template Object

The following code creates a template HTML element:

This template HTML element is a template for the shadow DOM HTML element that represents the HTML DOM of the Styling Panel of the Gauge Box.

3.2 JavaSript API of the Custom Element

  • Constructor
    The first function in the JavaScript API is the constructor.
    The super() function is called, then the shadow DOM root element is created. The 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.The submit() function is implemented as follows:

    The _submit() function calls function preventDefault() on the passed event object, which prevents submitting the form to the server.
    Then, a custom event propertiesChanged is created,  indicates a change of properties to the Custom Widget SDK framework. This custom event contains a JSON payload which is the color property of the custom widget.
  • Getters and Setters Property
    The following code shows the implementation of color setter and getter functions.

    The color setter function places a text representation of the new color into the input field of the Gauge Box’s Styling Panel.
    The color getter function returns the text of the input field (color value) of the Gauge Box’s Styling Panel.

4. Web Components JavaScript of the Builder Panel (box_bps.js)

This Builder Panel lets you change the text color of the Gauge Box in analytics designer.

The code is very similar to the Web Components JavaScript of the Styling Panel. The following code shows the Web Component JavaScript of the Builder Panel (box_bps.js).

(function()  {
	let template = document.createElement("template");
	template.innerHTML = `
		<form id="form">
			<fieldset>
				<legend>Color Properties</legend>
				<table>
					<tr>
						<td>Color</td>
						<td><input id="bps_color" type="text" size="10" maxlength="10"></td>
					</tr>
				</table>
				<input type="submit" style="display:none;">
			</fieldset>
		</form>
		<style>
		:host {
			display: block;
			padding: 1em 1em 1em 1em;
		}
		</style>
	`;

	class BoxBps 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("bps_color").value = newColor;
		}

		get color() {
			return this._shadowRoot.getElementById("bps_color").value;
		}
	}

	customElements.define("com-demo-box-bps", BoxBps);
})();

The Web Component JavaScript defines a new custom element com-demo-box-bps. The JavaScript API of the new custom element is implemented in the BoxSps class which extends the JavaScript API of the HTMLElement class.

4.1 Template Object

The following code creates a template HTML element:

This template HTML element is a template for the shadow DOM HTML element that represents the HTML DOM of the Builder Panel of the Gauge Box.

4.2 JavaSript API of the Custom Element

  • Constructor
    The first function in the JavaScript API is the constructor.
    The super() function is called, then the shadow DOM root element is created. The 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.The submit() function is implemented as follows:

    The _submit() function calls function preventDefault() on the passed event object, which prevents submitting the form to the server.
    Then, a custom event propertiesChanged is created,  indicates a change of properties to the Custom Widget SDK framework. This custom event contains a JSON payload which is the color property of the custom widget.
  • Getters and Setters Property
    The following code shows the implementation of color setter and getter functions.

    The color setter function places a text representation of the new color into the input field of the Gauge Box’s Builder Panel.
    The color getter function returns the text of the input field (color value) of the Gauge Box’s Builder Panel.

Construct All Files Together

Once we have created all the required files, we need to organize in the following structure:

  • Create a folder called customwidgets and put box.json into that folder.
  • Create another folder box inside customwidgets folder and put the rest of the files inside box folder.
  • Upload all files to the web server.

Adding the Custom Widget to Analytics Designer

  • In the Analytics Designer, navigate to Main Menu > Browse > Custom Widgets. If you don’t see this option, check your security roles.
  • Click the Create toolbar icon.
  • In the Upload File dialog, click the Select File button.
  • Select the custom widget JSON file box.json.
  • Create a new analytic application. The custom widget is listed in the widget list of the dropdown menu of the Add menu.

Using the Custom Widget in Analytics Designer

Once you have added the Gauge custom widget, you can see the following functions.

The below code illustrates who to define the parameters for the setValue function. And how to call the getValue function.

ScriptVariable_1 = ScriptVariable_1 + 1;
ScriptVariable_2 = ScriptVariable_2 - 1;

//Set value
Gauge_1.setValue(ScriptVariable_1, "Plus Gauge 1", "#e74c3c");
Gauge_2.setValue(ScriptVariable_2, "Plus Gauge 2", "#3498db");

//Get percentage value
console.log(Gauge_1.getValue());

See it in action:

Here is another demo of Google Gauge widget:

Chill the Lion:

Integration between amCharts and SAC:

Another integration with amChart and SAC:

Highcharts 3D Cylinder:

Google Map widget:

YouTube Player widget:

Multiple Instances of Google Gauges widget:

Another version of Google Map widget:

Singapore map widget:

Clock widget:

Geo Chart widget:

Rotate Globe widget:

Another Rotate Globe widget:

Liquid Fill Gauge widget:

QR code generator widget:

Word Cloud widget:

Solid Gauge widget:

Amchart Gauge widget:

Heat map widget:

Compass widget:

Text widget:

Linear Gauge widget:

Three.js custom widget:

3D object widget (with mouse controls):

 

3D scatter widget:

Bubble chart widget:

Barcode generator widget:

Pie Chart widget:

Day & Night World Map widget:

amCharts Serpentine timeline:

Column with Rotated Series from amCharts:

Stadium track chart from amCharts:

Radar Timeline chart from amCharts:

Dragging Pie Slices from amCharts:

Pictorial Chart from amCharts:

Radial Bar Chart with Highcharts:

Surface Chart with Anychart:

Seat Map Chart with Anychart:

Connector Map with Anychart:

Gauge with FusionCharts:

Cylinder Fill Chart with FusionCharts:

Gantt Chart with FusionCharts:

Map chart with FusionCharts:

Gauges with FusionCharts:

Google Poly:

Org chart with FusionCharts:

Radar (Spider) custom widget with FusionCharts:

A quick demo video to control the SAP Analytics Cloud Custom Widget with web Bluetooth and micro:bit. User presses the button A and B on micro:bit to fill in and out the cylinder gauge.

Control two Google Gauges widgets with two micro:bits and Web Bluetooth:

Grouped Location Chart with ZoomCharts:

Another custom widget with ZoomCharts:

Google Translate Custom Widget:

Currency conversion custom widget with Google Finance:

Display stock price information from Google Sheets function =GOOGLEFINANCE(“NYSE:BABA”,“price”,TODAY()180,TODAY())”

Currency exchange trends:

Object detection with TensorFlow JS:

 

Tracking Coronavirus COVID-19 with Here:

Novel Coronavirus (COVID-19) Infection Map:

3D map with wrld3d.com:

Real-time temperature monitoring with WhatsApp notification:

SAPUI5 Date Picker Custom Widgets:

SAPUI5 KPI Tile Custom Widget:

SAP ChatBot Custom Widget with SAP Conversational AI:

SAPUI5 sap.suite.ui.commons.networkgraph.Graph:

SAC Visual Tree Mapper:

SAC Widget to Send and Receive Messages with RabbitMQ:

SAC Widget to Send and Receive Messages with Kafka:

Generic Tile Custom Widget:

Launch Zoom Meeting from Analytic App:

Custom Chat Bot Widget:

Covid-19 Widget:

Real Time Notification Custom Widget:

Zoom Meeting:

More Tutorials Related to SAC Custom Widget

Reference

SAP Analytics Cloud Custom Widget Developer Guide

SAC Custom Widget Facebook Page

Assigned Tags

      60 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Praveen singh
      Praveen singh

      Hi Ferry, Is there a way to include additional javascript libraries into our custom widget json so that I can make use of them in my component javascript file. It was mentioned in SDK docs that we can include javascript libraries but the way to include those are not mentioned. Thanks in advance

       

      Author's profile photo Ferry Djaja
      Ferry Djaja
      Blog Post Author

      hi Praveen,

      You may check this SO: https://stackoverflow.com/questions/50698021/using-external-js-libraries-in-a-web-component. I updated my blog with the demo of Google Gauge.

      Cheers,

      Ferry

      Author's profile photo Functional Support
      Functional Support

      hi Ferry,

       

      All your Demos look super cool. The Google Gauge demo is exactly what am looking for. I would be grateful if you can share some further information on how the google library could be integrated into the custom widget web component. Did you skip the shadow dom completely? Also, did you try experimenting with Polymer? What build tools do you use?

      Any pointers would be appreciated.

       

      Thanks in advance!

      Ramu

      Author's profile photo Ferry Djaja
      Ferry Djaja
      Blog Post Author

      hi Ramu,

      I am using pure JS and no other libraries. The reason is the loading is faster and the size is very small. Yes I am using shadow DOM.

      https://www.sapanalytics.cloud/product_updates/developer-future/

      Cheers,
      Ferry

      Author's profile photo M. Bruins
      M. Bruins

      Hi Ferry,

      is there any information on how to bind or get data from a model in stead of only feeding data to the custom widget with scripting functions?

      Author's profile photo Carlos Suarez
      Carlos Suarez

      I believe data binding is not possible yet.

      It is indeed in the planned innovations for 2020 if I recall correctly. I believe David Stocker mentioned this during his custom widget hands on session in teched 19.

      Author's profile photo Ferry Djaja
      Ferry Djaja
      Blog Post Author

      https://www.sapanalytics.cloud/product_updates/developer-future/

      Author's profile photo Armando Santos
      Armando Santos

      Hello Ferry

       

      Great stuff what you are sharing. Thanks a million!!!

       

      I am trying myself to create my first widget (based on your "com.demo.gauge") but I am struggling with an issue.

      I've uploaded the files:

      box.json

      box\box.js

      box\box_bps.js

      box\box_sps.js

      box\icon.png

      into: https://try.gogs.io/armando.j.a.santos/SAPCustomWidget/src/master/box/

      but after I upload the custom  widget in SAC and add this widget into a new Analytic Application then I get the below error:

      "Something went wrong. The system couldn't load the custom widget 'com.demo.gauge_1.x' (kind: 'main') for this reason: The system took too long to define the custom widget.'

       

      Can I ask your help? Can you please tell me what I am doing wrong?

       

      Regards

      Armando Santos

      armando.j.a.santos@gmail.com

       

      Content of my json file is:

      {
      	"id": "com.demo.gauge",
      	"version": "1.0.0",
      	"name": "Demo Gauge",
      	"description": "A gauge demo",
      	"newInstancePrefix": "Gauge",
      	"icon": "https://try.gogs.io/armando.j.a.santos/SAPCustomWidget/src/master/box/icon.png",
      	"vendor": "Demo",
      	"eula": "EULA",
      	"license": "1.0",
      	"webcomponents": [
      		{
      			"kind": "main",
      			"tag": "com-demo-gauge",
      			"url": "https://try.gogs.io/armando.j.a.santos/SAPCustomWidget/src/master/box/box.js",
      			"integrity": "",
      			"ignoreIntegrity": true
      		},
      		{
      			"kind": "styling",
      			"tag": "com-demo-gauge-sps",
      			"url": "https://try.gogs.io/armando.j.a.santos/SAPCustomWidget/src/master/box/box_sps.js",
      			"integrity": "",
      			"ignoreIntegrity": true
      		},
      		{
      			"kind": "builder",
      			"tag": "com-demo-box-bps",
      			"url": "https://try.gogs.io/armando.j.a.santos/SAPCustomWidget/src/master/box/box_bps.js",
      			"integrity": "",
      			"ignoreIntegrity": true
      		}
      	],
      	"properties": {
      		"value": {
      			"type": "number",
      			"description": "Gauge value",
      			"default": "0"
      		},
      		"info": {
      			"type": "string",
      			"description": "Gauge info",
      			"default": ""
      		},
      		"color": {
      			"type": "string",
      			"description": "Text Color info",
      			"default": "#3498db"
      		},
      		"width": {
      			"type": "integer",
      			"default": 100
      		},
      		"height": {
      			"type": "integer",
      			"default": 100
      		}
      	},
      	"methods": {
      		"setValue": {
      			"description": "Sets the Gauge value.",
      			"parameters": [
      				{
      					"name": "newValue",
      					"type": "number",
      					"description": "Gauge value"
      				},
      				{
      					"name": "newInfo",
      					"type": "string",
      					"description": "Gauge info"
      				},
      				{
      					"name": "newColor",
      					"type": "string",
      					"description": "Text Color info"
      				}
      			],
      			"body": "this.value = newValue; this.info = newInfo; this.color = newColor;"
      		},
      		"getValue": {
      			"returnType": "number",
      			"description": "Returns the Gauge value.",
      			"body": "return this.value;"
      		}
      	},
      	"events": {
      		"onClick": {
      			"description": "Called when the user clicks the Box."
      		}
      	}
      }
      

       

      Author's profile photo Ferry Djaja
      Ferry Djaja
      Blog Post Author

      hi Armando,

      Can you please check and ensure that SAC can access this URL:

      https://try.gogs.io/armando.j.a.santos/SAPCustomWidget/src/master/box/box.js

      Maybe you can try to host the codes in Git and see.

      Cheers,

      Ferry

      Author's profile photo Armando Santos
      Armando Santos

      Hello Ferry

       

      I have also tried with GitHub:

      https://github.com/armando-j-a-santos/SAPCustomWidget2/blob/master/coloredbox.json

      And it give me exactly the same error message mentioned earlier.

      Kindly assist me.

      Regards

      Armando Santos

      Author's profile photo Armando Santos
      Armando Santos

      Hello Ferry

      Let me add into this issue, the fact that all JS files and JSON files are accessible in GitHub without authentication (public files).
      My guess is that something - some kind of configuration, is missing is SAC side. Do I need some extra step in SAC settings?

      Kindly help me.

      Armando Santos

      Author's profile photo Ferry Djaja
      Ferry Djaja
      Blog Post Author

      hi Armando,

      Can you ignore that error message and click "Run Analytics Application" and check if you can see the chart ?

      Cheers,

      Ferry

      Author's profile photo Armando Santos
      Armando Santos

      Good morning Ferry

       

      If I run the analytics app then na blank page appears and the error also appears - please see attached image.

      Maybe there is something wrong on the scripting code (JS and JSON files). Do you want me to send you a copy of my code?

      On the other hand, I still believe there is something missing in SAC settings that I am not aware. Can you please advice?

       

      Kind regards

      Armando Santos

      Author's profile photo Ferry Djaja
      Ferry Djaja
      Blog Post Author

      Hi Armando,

      The code is in Git in this blog. Can you do F12 and see what's the error details ?

      Cheers,
      Ferry

      Author's profile photo Armando Santos
      Armando Santos

      Sure. Here it is.

      Any suggestion dear Ferry?

      Constructor issue? (please have a look in Git code)

      Regards

      Armando Santos

      Author's profile photo Ferry Djaja
      Ferry Djaja
      Blog Post Author

      It looks like in coloredbox_builder.js your there is no customElements.define.

       

      Author's profile photo Armando Santos
      Armando Santos

      Thanks Ferry.

       

      You are right. I was missing the customElements.define in the builder file - THANKS!, but even after ignoring the error and if I run the application it still throws the same error (F12 is showing the same errors as in previous post).

      Any suggestion is more than welcome dear Ferry?

       

      Regards

      Armando Santos

      Author's profile photo Hau Ngo
      Hau Ngo

      Thanks for the guide, Ferry Djaja ! ?

      I was experiencing the same issue that Armando Santos had described.

      However, I ran the application and looked at the console log as you suggested and discovered it was an SSL issue: https://d.pr/i/egX16s

      After applying a free SSL certificate to my domain, updating the URL in the JSON file, and uploading the new JSON file to my SAP Analytics Cloud tenant ... I am able to use the custom widget for the gauge chart type ? https://cloud.summerlinanalytics.com/2Nurjl10

      I hope this helps anyone else who is trying to use the custom widgets!

      Author's profile photo Armando Santos
      Armando Santos

      Thanks Hau for your valuable help.

      In your case the error seems to be easier to understand and to fix as well:
      "but requested an insecure script 'http://summerlinanalytics.com/customwidgets/gauge/box.js". This request has been blocked; the content must be served over HTTPS."

      We can clearly see this is an SSL issue.

      But in my example - within JSON file - I am using and HTTPS WEB server:
      "https://github.com/armando-j-a-santos/SAPCustomWidget2/blob/master/coloredbox.js"
      And after deleting the Widget in SAC, upload the new JSON file to my tenant and use it in SAC application I still get the same issue.

      Any suggestion is more than welcome dear Hau/Ferry.

      Regards
      Armando Santos

      Author's profile photo Armando Santos
      Armando Santos

      I don't know if it helps or not but in my test application in SAC onInitialization() I have the below code:

      var ScriptVariable_1 = 10;
      
      //Set value
      Gauge_1.setValue(ScriptVariable_1, "Plus Gauge 1", "#e74c3c");
      
      //Get percentage value
      console.log(Gauge_1.getValue());

      And I can see the Gauge_1 widget icon (please see attached image), so it means the icon.PNG file is being read from my Web Server GitHub (HTTPS). However I still get the same errors messages as shown in previous posts.

       

      Any suggestion is more than welcome dear Hau/Ferry.

      Regards
      Armando Santos

      Author's profile photo Armando Santos
      Armando Santos

      @FERRY: One more try from my side! I have just used your JS files (from github) within my JSON file:

      "id": "com.demo.gauge",
      	"version": "1.0.0",
      	"name": "Demo Gauge",
      	"description": "A gauge demo",
      	"newInstancePrefix": "Gauge",
      	"icon": "https://github.com/ferrygun/SAPCustomWidget/blob/master/box/icon.png",
      	"vendor": "Demo",
      	"eula": "EULA",
      	"license": "1.0",
      	"webcomponents": [
      		{
      			"kind": "main",
      			"tag": "com-demo-gauge",
      			"url": "https://github.com/ferrygun/SAPCustomWidget/blob/master/box/box.js",
      			"integrity": "",
      			"ignoreIntegrity": true
      		},
      		{
      			"kind": "styling",
      			"tag": "com-demo-gauge-sps",
      			"url": "https://github.com/ferrygun/SAPCustomWidget/blob/master/box/box_sps.js",
      			"integrity": "",
      			"ignoreIntegrity": true
      		},
      		{
      			"kind": "builder",
      			"tag": "com-demo-box-bps",
      			"url": "https://github.com/ferrygun/SAPCustomWidget/blob/master/box/box_bps.js",
      			"integrity": "",
      			"ignoreIntegrity": true
      		}
      	],
      ...

       

      And after deleting the widget in SAC, upload the new JSON file to my tenant and use it in a SAC application I still get the same the error (the same as mention earlier in the beginning of my post):

      Please advice.

       

      Thanks in advance.

       

      Regards

      Armando Santos

      Author's profile photo Charlie Lin
      Charlie Lin

      Hello Armando,

      I was running the same error with you. And i fixed the issue by changing the GitHub settings. See the pic below.

      Then you will be able to visit the json file by using the URL. If you don't change the setting, the page will be like 3rd pic.

      https://shineshow.github.io/SAC/box/box.js

       

      Author's profile photo Armando Santos
      Armando Santos

      Bingo!!!

      Thanks a million Charlie for your valuable tip.
      This was the setting that I needed. I thought it was something in SAC side but in fact it was in github side.

      One more time thanks Charlie.

      Regards
      Armando Santos

      Author's profile photo Sebastian Preusser
      Sebastian Preusser

      Hey all,

      I am facing a different errror:

      In my case the styling is the issue.

       

      My JSON looks like this:

      "id": "com.demo.gauge",
      "version": "2.0.0",
      "name": "Demo GaugeV2",
      "description": "Demo",
      "newInstancePrefix": "Gauge",
      "icon": "https://sebastian684.github.io/SAPCustomWidget/box/icon.png",
      "vendor": "Demo",
      "eula": "EULA",
      "license": "1.0",
      "webcomponents": [
      {
      "kind": "main",
      "tag": "com-demo-gauge",
      "url": "https://sebastian684.github.io/SAPCustomWidget/box/box.js",
      "integrity": "",
      "ignoreIntegrity": true
      },
      {
      "kind": "styling",
      "tag": "com-demo-gauge-sps",
      "url": "https://sebastian684.github.io/SAPCustomWidget/box/box_sps.js",
      "integrity": "",
      "ignoreIntegrity": true
      },
      {
      "kind": "builder",
      "tag": "com-demo-box-bps",
      "url": "https://sebastian684.github.io/SAPCustomWidget/box/box_bps.js",
      "integrity": "",
      "ignoreIntegrity": true
      }
      ],

       

      Any ideas?

      Thanks,

      Sebastian

      Author's profile photo Functional Support
      Functional Support

      hi Sebastian,

      You have used the tag "com-demo-gauge-sps" in the JSON file, however in the box_sps.js has the tag defined as "com-demo-box-sps". that is why the error.

      Author's profile photo Andrew Barlow
      Andrew Barlow

      Hi Sebastian,

      Looking at the styles js file (box_sps.js) if you compare it with the builder js file (box_bps.js) it has some brackets missing from the end.

      I was experiencing the same issue but by adding })(); to the end of the styles js file (box_sps.js)  all works for me now without any error.

      Author's profile photo qingqing ji
      qingqing ji

      Hello,Ferry.

      Can you share souce file for the demo Google Gauge?

      Thanks.

       

      Author's profile photo Ferry Djaja
      Ferry Djaja
      Blog Post Author

      Hi Ji,

      I am afraid not able to share the code in this blog. Please ping me if you are interested.

      Regards,
      Ferry

      Author's profile photo Carolina Lasso Arce
      Carolina Lasso Arce

      Hello Ferry

      Can you share it with me?

      Thank.

      Author's profile photo Semih Baysal
      Semih Baysal

      Hi Ferry,

      How to add Data Source to custom widget
      
      Thanks.
      Author's profile photo Ferry Djaja
      Ferry Djaja
      Blog Post Author

      Hi Semih,

      I think that is not possible yet. Please check this: https://www.sapanalytics.cloud/product_updates/developer-future/

      Regards,
      Ferry

      Author's profile photo Arijit Das
      Arijit Das

      Can anyone please explain the meaning of the following code snippet:

      onCustomWidgetBeforeUpdate(changedProperties) {
      			this._props = { ...this._props, ...changedProperties };
      		}

      It gives syntax error in the JS editor.

       

      Thanks

      Arijit

       

      Author's profile photo Ferry Djaja
      Ferry Djaja
      Blog Post Author

      Hi Arijit,

      From the Help documentation:

      Regards,

      Ferry

      Author's profile photo Arijit Das
      Arijit Das

      Hi Ferry,

      Can we use webcomponents built with polymer framework to create custom widget in SAC? If yes, could you please share one simple example?

       

      Or, can we use third party js libraries like JQuery / D3 to create the custom widget? How to include those libraries and how to manipulate DOM elements inside shadow root using Jquery/D3?

       

      Thanks,

      Arijit

      Author's profile photo Ferry Djaja
      Ferry Djaja
      Blog Post Author

      Hi Arijit

       

      Yes you can blend it with the existing JavaScript libraries. Most of the examples I built, I am basing on the existing libraries and not building it from the scratch.

      Maybe I will post another blog on how to create a simple WebComponents and blend it with the external JS libraries like Amcharts.com  once I have time. To put in this write up alone, it will be very long and messy.

      Ping me if you are interested to know more details.

      Cheers,
      Ferry

      Author's profile photo Arijit Das
      Arijit Das

      Thanks Ferry for the hint. Now I am able to use third party JS libraries for the custom widget.

      Author's profile photo Ferry Djaja
      Ferry Djaja
      Blog Post Author

      welcome. I am just posted the use of external lib amCharts with the widget in SAC. Here is a quick video: https://youtu.be/CenR8D75nk8

      Author's profile photo VR Sunkara
      VR Sunkara

      Hey Ferry,

      All your demos look amazing. We are really struggling with building custom JS widgets using 3rd party libraries. I would greatly appreciate if you can give some pointers for the below questions:

      1. How did you manage to import external libraries into the web component? Any code snippets or tutorial links would help.
      2. In most of your demos, you were able to consume data from SAC model. Could you please give some hints on how to bind data into a custom widget? As per the SAP documentation, the input data types of the methods and parameters support only 1 dimensional arrays, how can we pass table like data into the custom widget?

       

      Any simple code snippets which shows the above concepts will really help with our POC. Am looking forward for your blog on Amcharts integrations. 🙂

       

      Thanks in advance!

      Venkat

      Author's profile photo Sungyoul Baik
      Sungyoul Baik

      Dear Ferry,

       

      The tutorial works perfect for me. Thank you a lot. I also like the examples you made!

      I'm trying you implement 3rd part chart library on the custom widget just like you did, but I have no clue how to embed the chart libraries into the custom widget files. I know you are about to write a blog about it but can you share a sample code please?

       

      Many thanks.

      Author's profile photo Ferry Djaja
      Ferry Djaja
      Blog Post Author

      Hi Sungyoul,

       

      You may try this to load the external lib:

      const script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = src;
      script.addEventListener("load", callback);
      shadowRoot.appendChild(script);

      Cheers,

      Ferry

      Author's profile photo Sevim Eraslan
      Sevim Eraslan
      Hi Ferry,
      
      I also wanted to test your files in SAP Analytics Cloud. The following error message occurs when uploading Box.json. 
      
      
      
      Do you know what this means?
      
      Best regards
      Sevim
      
      Author's profile photo Oguz Kose
      Oguz Kose

      I changed the box.json file. There is value block in properties area.

      	"properties": {
      		"value": {
      			"type": "integer",
      			"description": "Gauge value",
      			"default": 0
      		},
      

      Its looking like that now. You can try this method.

      Author's profile photo Fabio Sist
      Fabio Sist

      Hi Ferry,

      is there a way to modify the Gauge box properties inside the box.js? Let's say I'd like to change the color property based on the value of the gauge, is it possible to do it on widget side (box.js) instead of managing it on Analytic Designer side?

      Note that I'd like to change the propery in the way that the getter method will give me the new value modified in the box.js.

      Fabio

      Author's profile photo Aleksey Salinin
      Aleksey Salinin

      Ferry Djaja firstly, thanks a lot!

      have u any instructions or step-by-step on how to embed m.components in App with CW? For example, DateTime.Picker or KPI.Tiles (like in your youtube videos)?

      Author's profile photo Ferry Djaja
      Ferry Djaja
      Blog Post Author

      hi Aleksey Salinin

      Stay tuned!!, I will publish how-to tutorial some time!!

      Regards,
      Ferry Djaja

      Author's profile photo Huimin Li
      Huimin Li

      Thank you so much Ferry, all the blogs you posted about custom widget on SAC is so helpful!!

      Author's profile photo Ferry Djaja
      Ferry Djaja
      Blog Post Author

      Hi Aleksey Salinin

       

      You can check my new blog here on how to use SAPUI5 elements in SAP Analytics Cloud Custom WIdget.

      https://blogs.sap.com/2020/06/02/add-and-use-sapui5-elements-in-sap-analytics-cloud-custom-widget

       

      Regards,

      Ferry Djaja

      Author's profile photo Surya Dharma
      Surya Dharma

      Halo Ferry Djaja terima kasih untuk postingnya!

      Do you have customise trend indicator arrow tutorial for analytics design, maybe? that will show the arrow trend if the value below 3% (or absolute number) the arrow will be red, more than 3% will green and in between will show a deep yellow colour with a horizontal arrow shape. Thanks in advance for any advice!

       

      Regards,

      Surya Dharma

      Author's profile photo Ferry Djaja
      Ferry Djaja
      Blog Post Author

      Hi Surya Dharma

      Terima kasih.

      If using custom widget, we can define all those parameters in the styling or builder panel. I think is easy to achieve it. I don't have that tutorial yet at this moment. I will find some time to write it.

      Regards,

      Ferry Djaja

      Author's profile photo Surya Dharma
      Surya Dharma

      Halo Ferry Djaja,

      That would be great, Ferry! really appreciate if you can share the techniques. But maybe I describe it clear what I want to achieve.

      So I'm goint to build a dashboard and might be use the structure as template structure. I need to build this in analytics designer mode (not story). The source is queries with live-connection. This dashboard have 40+ (numeric chart) KPI's. Build with 6 Panel. On initial the first three panels appears and switch to another panel with radiobutton (this is done). Each Panel contain four KPI's. On init the indicator should read each KPI value and show correspondent arrow trend indicator (this is still homework). Later on I will build dropdown for year, month, week, etc filter.

      How I've done so far. I added three different gif images (green, red, yellow) on each KPI's. So, here are 40+ KPI's multiply by three.  It's a lot small images placed by the KPI side-by-side. I have two global variable, one to control image, and the other one to control the value. I created global script for expected condition (>3%, below and in between). Still the result not really working as my expectation.

      I don't find another solution so far, like using "Form" (circle shape) and controll the colour pallete through script (like ".setColor function). Well the setColor function is not available and furthermore there is only "onClick" event with "shape" instead of something like "onResultChanged".

      Appreciate a lot if you can share or have any method to create less-complex trend arrow indicator. Terima kasih!

       

       

      Regards,

      Surya Dharma

      Author's profile photo Ferry Djaja
      Ferry Djaja
      Blog Post Author

      Hi Surya,

       

      if you could share your code, I will try to figure it out.

      Regards,

      Ferry Djaja

      Author's profile photo Roopa Puranik
      Roopa Puranik

      Hi Ferry,

      I was researching and came across your article. I am trying to see if I can create a custom widget to reset story in SAC and use the rest button widget in the dashboard. Is that doable. If so, what is the code and if you can provide details on how to accomplish this, that would be great? Our users hate the reset icon that is available on the toolbar and its not very visible. They all have been asking for the reset button to be added to the dashboard on all tabs. We have a big demo coming u next week and want to impress our execs, so any help would be greatly appreciated.

      Thank you

      Roopa

       

      P.S. I have an idea submitted for this in the idea place already to highlight the reset button and call it reset.

       

       

      Author's profile photo Ferry Djaja
      Ferry Djaja
      Blog Post Author

      hi Roopa,

       

      Can you let me know what is the reset function in SAC story ?

       

      Regards,
      Ferry

      Author's profile photo Roopa Puranik
      Roopa Puranik

      Hi Ferry,

       

      To Reset all the filters on the story or on the tab where the widget is added. Same as the Reset button on the toolbar in SAC stories.

      This will be a life saver until SAP enhances the current reset icon.

      Thank you so much!

      Roopa

      P.S. The story is not built using Analytics Designer. 

      Author's profile photo Ferry Djaja
      Ferry Djaja
      Blog Post Author

      hi Roopa,

       

      I am not sure if there is any API for reset function in SAC Analytic Application. If there is, we can create a widget for that purpose.

      Regards,

      Ferry

      Author's profile photo Roopa Puranik
      Roopa Puranik

      Okay Thank you. I don't know a whole lot about JAVA scripting but was hoping we could accomplish this by creating a custom widget and use it in SAC stories.

      Author's profile photo Martino Rivaplata
      Martino Rivaplata

      Hi Ferry,

      I have an SAC analytic application in which I have a button on it.  I would like for the button onClick event to trigger refreshing the https:// URL page of the Analytic Application.  When you have the chance if you have any ideas please let me know. Attached is an screenshot of the analytic application. Please let me know any questions. Thank You!

       

      Author's profile photo Roopa Puranik
      Roopa Puranik

      Hello Ferry,

      Do you know if it's possible to setup a story page with carousel style page (slider/slideshow) using application designer? Can you please share some insights on this.

      Thank you

      Roopa

      Author's profile photo Maxime Gillet
      Maxime Gillet

      Hello ,

      Thanks for the blog it is great!

      Is there a secure way to handle credentials? I mean if the button is doing an http request on a protected url, how should it be done?

      Thanks

      Author's profile photo Francesco Allocca
      Francesco Allocca

      Hi Ferry,

      If we would modify the chart to have a range between -100% to 100%? And not 0% to 100%.

      How can we modify the code?

      We were tryng but doesn't work

       

      Thanks

      Francesco