Skip to Content
Technical Articles
Author's profile photo M Ahmad

D3’s Bubble Chart in UI5


With the reference to my previous blog on D3’s map (link below):

I thought of writing this blog on “Bubble Chart in UI5″, I followed the similar coding style here, where I have locally stored the data in model and building the chart using custom control.

Bubble Chart’s code & its sample test data I have referred from below link:


  • SAP Web IDE Full-Stack
(Disclaimer: Web IDE is discontinued and can no longer be purchased for new customers, and that BAS is the default development environment)


Final output in UI5 will look as below:

Technical Code:

I am not following here step by step approach on how to create app, view, controller etc., because it will make the blog long, so summarizing technical details as below:

App technical name is : “ZDEV_BUBBLE” with below folder structure :



model(folder) contains data;

  • “TestData.json” : It contains data in below format, where we have only two fields; “id” & “value”
"id": "",
"value": 3938
}, {
"id": "",
"value": 3812

in “id” we can have our own custom values for example : “2023.Material Group.Material Name”

Other than standard folder structure we have created two additional folders below:

  • “controls” – this folder contains custom control JavaScript coding for implementing bubble chart
  • “thirdparty” – this folder contains the JavaScript libraries d3.js


  • Get data logic and bubble chart related logic is written in custom control & main view only the complete code snippet of the view and custom control is copied below
  • Test data and JS libraries you can get from below link:



	<Page id="page" title="{i18n>title}">
	    <controls:D3Viz data="{/ui5con}">
	        <base:ManagedObject />


Custom Control (D3Viz.js):


], function (Control, HTML, ResizeHandler, JSONModel, jQuery) {
	"use strict";

	return Control.extend("ns.ZDEV_BUBBLE.controls.D3Viz", {

		metadata: {
			aggregations: {
				_html: {
					type: "sap.ui.core.HTML",
					multiple: false,
					visibility: "hidden"
				data: {
					type: "sap.ui.base.ManagedObject"

		init: function () {
			this._sContainerId = this.getId() + "--container"
			this.setAggregation("_html", new HTML({
				content: "<svg id='" + this._sContainerId + "'></svg>"

		exit: function () {

		renderer: {
			apiVersion: 2,
			render: function (oRm, oControl) {
				oRm.openStart('div', oControl);

		_onResize: function () {

		onBeforeRendering: function () {

		onAfterRendering: function () {
			this._sResizeHandlerId = ResizeHandler.register(this, this._onResize.bind(this));

		_renderViz: function () {

			const height = 600;
			const width = this.$().width();

			//Select container
			const svg ='#' + this._sContainerId);
			svg.attr("height", height).attr("width", width);
			svg.attr("style", "max-width: 100%; height: auto; font: 10px sans-serif;");
			svg.attr("text-anchor", "middle");

			var oModel = new JSONModel();
			var sPath ="ns.ZDEV_BUBBLE", "/model/TestData.json");
				url: sPath,
				dataType: "json",
				async: false, // Synchronous loading for simplicity (not recommended in production)
				success: function (oData, res) {

					const data = oData;

					 * The following snippet is based on the Bubble Chart of Mike Bostock
					const margin = 1; // to avoid clipping the root circle stroke
					const name = d =>".").pop(); // "Strings" of "flare.util.Strings"
					const group = d =>".")[1]; // "util" of "flare.util.Strings"
					const names = d => name(d).split(/(?=[A-Z][a-z])|\s+/g); // ["Legend", "Item"] of "flare.vis.legend.LegendItems"

					// Specify the number format for values.
					const format = d3.format(",d");

					// Create a categorical color scale.
					const color = d3.scaleOrdinal(d3.schemeTableau10); 
					// Create the pack layout.
					const pack = d3.pack()
						.size([width - margin * 2, height - margin * 2])
					// Compute the hierarchy from the (flat) data; expose the values
					// for each node; lastly apply the pack layout.
					const root = pack(d3.hierarchy({
							children: data
						.sum(d => d.value));

					// Place each (leaf) node according to the layout’s x and y values.
					const node = svg.append("g")
						.attr("transform", d => `translate(${d.x},${d.y})`);

					// Add a title.
						.text(d => `${}\n${format(d.value)}`);

					// Add a filled circle.
						.attr("fill-opacity", 0.7)
						.attr("fill", d => color(group(
						.attr("r", d => d.r);
					// Add a label.
					const text = node.append("text")
						.attr("clip-path", d => `circle(${d.r})`);

					// Add a tspan for each CamelCase-separated word.
						.data(d => names(
						.attr("x", 0)
						.attr("y", (d, i, nodes) => `${i - nodes.length / 2 + 0.35}em`)
						.text(d => d);

					// Add a tspan for the node’s value.
						.attr("x", 0)
						.attr("y", d => `${names( / 2 + 0.35}em`)
						.attr("fill-opacity", 0.7)
						.text(d => format(d.value));
				error: function (err) {
					console.log(err); // Handle the error if loading fails

		} // end of onAfterRendering



I hope this blog will help others to implement D3 charts/maps with the help of custom control in UI5 with less efforts.

I welcome any suggestion or improvements for the code and other feedback.


Thank you.



Masoom Ahmad


Assigned Tags

      Be the first to leave a comment
      You must be Logged on to comment or reply to a post.