Hello everyone, welcome back to our blog series on transforming SAP Analytics Cloud (SAC) with custom widgets.
Our previous blogs can be found here:
1. Transforming SAC with Custom Widgets (Part 1) – link
2.Transforming SAC with Custom Widgets (Part 2 – Gamify) – link
3.Building a Customizable Calculator Widget for SAC – Styling and Builder Panel – link 4. Transforming SAC with Custom Widgets (Part 4 – Custom Widgets Data Binding)– link
By the way, we encourage you to follow us on [LinkedIn]. For every 100 new followers, we promise to create another widget for free! This widget will be developed through a democratic process – we will run a vote, and the most popular choice will be implemented. :)
In this blog post, we will continue our journey of creating custom widgets for SAP Analytics Cloud (SAC). After successfully creating a Pie Chart widget in our previous post, we are now stepping up to create a more complex widget - a Gantt Chart. This widget will be useful for project management, scheduling tasks, and visualizing project timelines.
What is a Gantt Chart?
A Gantt chart is a type of bar chart that illustrates a project schedule. It visualizes tasks over time, showing start and end dates, as well as dependencies between tasks. Gantt charts are widely used in project management and other planning contexts.
Our Goal
Our goal is to create a Gantt Chart widget that can be used directly in SAC. We will use the DHTMLX Gantt library, which provides a powerful and flexible way to create Gantt charts.
Getting Started
First, we need to create a new HTML element for our widget. We do this by extending the `HTMLElement` class in JavaScript. We also create a `template` element that will hold the HTML and CSS for our widget.
```javascript
let tmpl = document.createElement('template');
tmpl.innerHTML = `
<style>
#chart {
border: 1px solid #000;
padding: 10px;
margin: 10px;
width: 100%;
max-width: 95%;
height: 500px;
overflow: hidden;
box-sizing: border-box;
}
</style>
<div id="chart"></div>
<a href="https://www.linkedin.com/company/planifyit" target="_blank" class="follow-link">Follow us on Linkedin - Planifyit</a>
`;
class GanttChartWidget extends HTMLElement {
constructor() {
super();
console.log('Constructor called');
this._shadowRoot = this.attachShadow({mode: 'open'});
this._shadowRoot.appendChild(tmpl.content.cloneNode(true));
this._props = {};
this.tasks = [];
}
}
```
In the `constructor` method, we attach a shadow root to the element and append a clone of our template's content to it. This allows us to encapsulate the style and structure of our widget. You can view the full JavaScript code [here].
Understanding the JSON Configuration File
In the context of our Gantt chart widget, the JSON file plays a crucial role in defining the widget's properties, methods, events, and data bindings. This file is essentially the blueprint of our widget, providing SAP Analytics Cloud with the necessary information to correctly render and interact with the widget.
Let's break down the key components of our `GanttChartWidget.json` file:
{
"name": "GanttChartWidget",
"description": "Gantt Chart Widget",
"newInstancePrefix": "GanttChartWidget",
....
"icon": https://planifyit.github.io/Pie_chart/PlanifyIT_Logo2.png",
"vendor": "Planifyit",
"webcomponents": [
{
"kind": "main",
"tag": "gantt-chart-widget",
"url": "https://planifyit.github.io/Gantt_Chart/GanttChartWidget.js",
"integrity": "",
"ignoreIntegrity": true
}
],
.....
"dataBindings": {
"myDataBinding": {
"feeds": [
{
"id": "dimensions",
"description": "Dimensions",
"type": "dimension"
},
{
"id": "measures",
"description": "Measures",
"type": "mainStructureMember" }
....
You can view the full JSON code [here].
name, description, newInstancePrefix, eula, license, id, version, icon, vendor: These fields provide basic information about the widget, such as its name, description, version, and the vendor who created it. The `icon` field specifies a URL to an image that will be used as the widget's icon in SAP Analytics Cloud.
webcomponents: This section specifies the web components that make up the widget. In our case, we have one main component, which is defined in the `GanttChartWidget.js` file. The `url` field specifies the URL where this file can be found.
dataBindings: This section defines the data bindings of the widget. Data bindings allow the widget to receive data from SAP Analytics Cloud. In our case, we have one data binding called `myDataBinding`, which can receive dimensions and measures from the data model.
This JSON file is crucial for the functioning of our widget. It tells SAP Analytics Cloud how to interact with our widget, what data it can accept, and how it should be displayed.
Loading the DHTMLX Gantt Library
Next, we need to load the DHTMLX Gantt library. We do this by creating `script` and `link` elements and appending them to our shadow root. We set the `src` attribute of the `script` element to the URL of the DHTMLX Gantt JavaScript file, and the `href` attribute of the `link` element to the URL of the DHTMLX Gantt CSS file.
```javascript
// Load DHTMLX Gantt CSS
const dhtmlxGanttCSS = document.createElement('link');
dhtmlxGanttCSS.rel = 'stylesheet';
dhtmlxGanttCSS.href = 'https://cdn.dhtmlx.com/gantt/edge/dhtmlxgantt.css';
this._shadowRoot.appendChild(dhtmlxGanttCSS);
// Load DHTMLX Gantt
const dhtmlxGanttScript = document.createElement('script');
dhtmlxGanttScript.src = 'https://cdn.dhtmlx.com/gantt/edge/dhtmlxgantt.js';
dhtmlxGanttScript.onload = () => {
this._dhtmlxGanttReady = true;
this._renderChart();
};
this._shadowRoot.appendChild(dhtmlxGanttScript);
```
When the DHTMLX Gantt script has finished loading, we set a flag (`this._dhtmlxGanttReady`) to `true` and call a method (`this._renderChart()`) to render the chart.
Defining the Widget's Properties
Next, we define the properties of our widget. In our case, we have one property: `myDataBinding`, which will hold the data for our Gantt chart. We define this property in the `metadata` static getter method.
```javascript
static get metadata() {
console.log('metadata called');
return {
properties: {
myDataBinding: {
type: "object",
defaultValue: {}
},
...
Updating the Widget's Properties
We also need to handle updates to our widget's properties. We do this in the `onCustomWidgetBeforeUpdate` and `onCustomWidgetAfterUpdate` methods. In `onCustomWidgetBeforeUpdate`, we merge the changed properties into our existing properties. In `onCustomWidgetAfterUpdate`, we check if the `myDataBinding` property has changed, and if so, we update our data.
```javascript
onCustomWidgetBeforeUpdate(changedProperties) {
console.log('onCustomWidgetBeforeUpdate called');
this._props = { ...this._props, ...changedProperties };
}
onCustomWidgetAfterUpdate(changedProperties) {
console.log('onCustomWidgetAfterUpdate called');
if ("myDataBinding" in changedProperties) {
const dataBinding = changedProperties.myDataBinding;
if (dataBinding.state === 'success') {
this._updateData(dataBinding);
}
```
Updating the Data
The `_updateData` method is where we process the data for our Gantt chart. We map over the data and create a new array of tasks, each with an `id`, `text`, `start_date`, `end_date`, `progress`, and `open` property.
```javascript
_updateData(dataBinding) {
console.log('_updateData called');
if (dataBinding && Array.isArray(dataBinding.data)) {
this.tasks = dataBinding.data.map((row, index) => {
// Process the data...
return {
.........
id: row.dimensions_0.label, // Unique id of task
text: row.dimensions_1.label, // Name of task
start_date: startDate, // Start date of task
end_date: endDate, // End date of task
progress: row.measures_0.raw, // Progress of task in percent
open: row.dimensions_4.id
};
...........
}).filter(Boolean);
this._renderChart();
}
}
```
Rendering the Chart
Finally, we render the chart in the `_renderChart` method. We first check if the DHTMLX Gantt library is ready, and if so, we initialize the Gantt chart and load our tasks into it.
```javascript
_renderChart() {
console.log('_renderChart called');
if (this._dhtmlxGanttReady) {
const chartElement = this._shadowRoot.getElementById('chart');
// Initialize the Gantt chart
gantt.init(chartElement);
// Load the tasks into the Gantt chart
gantt.parse({ data: this.tasks });
console.log('Gantt chart rendered');
}
}
```
Data Binding and Dynamic Data Population
One of the key features of our Gantt Chart Widget is its ability to dynamically populate data from a SAP Analytics Cloud (SAC) model using data binding. This allows the widget to display up-to-date project schedules based on the latest data in the SAC model.
Data binding in SAC is a process that establishes a connection between the UI of the widget (in this case, the Gantt chart) and the business data. Changes in the business data automatically update the UI, and vice versa.
In the context of our Gantt Chart Widget, the data binding is a one-way process. The widget receives data from SAC and displays it, but it does not send any data back to SAC. This means that any changes made directly in the Gantt chart (such as adding, modifying, or deleting tasks) do not affect the original data in SAC.
The data binding is set up in the `metadata` method of the widget. This method returns an object that defines the properties of the widget that can be bound to data in SAC. In our case, we have defined a single property, `myDataBinding`, which is of type "object" and has a default value of an empty object.
When the SAC model is updated, the `onCustomWidgetAfterUpdate` method is called. This method checks if the `myDataBinding` property is in the changed properties and, if so, calls the `_updateData` method with the new data.
SAC Model
Our SAC Model is rather simple and includes only the necessary dimensions
Debugging
As you can see the data is populated from SAC model Binding functionality:
Conclusion
Creating a Gantt Chart widget for SAC is a complex task, but with the help of the DHTMLX Gantt library, it becomes much more manageable. The resulting widget is a powerful tool for visualizing project timelines and managing tasks. We hope this guide has been helpful and encourage you to try creating your own custom widgets for SAC.
Outlook of the Gantt Chart Widget
One of the powerful features of the DHTMLX Gantt chart is its interactivity. It allows users to add, modify, and delete tasks directly in the chart. This can be a very useful feature for project managers and other users who need to adjust project schedules on the fly.
However, it's important to understand that these changes are not persistent in the context of our SAC widget. They are only stored in the memory of the browser and are lost as soon as you refresh the page. This is because the data binding from SAC to the widget is a one-way process. The widget receives data from SAC and displays it, but it does not send any data back to SAC. Therefore, any changes you make in the widget do not affect the original data in SAC.
This behavior might be confusing or misleading for users, who might expect their changes to be saved. If you want to make the changes persistent, you would need to implement a backend service that can store the changes and provide them to the widget along with the original data from SAC. This would involve a significant amount of additional development and is beyond the scope of a simple widget.
In most cases, the interactive features of the DHTMLX Gantt chart are used in standalone applications where the chart is connected to a database or other persistent data storage. In the context of a SAC widget, you might want to disable these features if they are not useful for your users. You can do this by configuring the chart appropriately when you initialize it. For example, you can set the `readonly` option to `true` to prevent the user from modifying the tasks.
_renderChart() {
console.log('_renderChart called');
if (this._dhtmlxGanttReady) {
const chartElement = this._shadowRoot.getElementById('chart');
// Initialize the Gantt chart
gantt.init(chartElement);
// Set the readonly property to true
gantt.config.readonly = true;
// Load the tasks into the Gantt chart
gantt.parse({ data: this.tasks });
console.log('Gantt chart rendered');
}
}
Integration with SAC
The widgets will be freely accessible on our Github page [Github]. We believe in open-source development and the power of community collaboration.
Just download the JSON file and upload it in your SAC tenant under Custom Widgets as follows:
Integrating the Gantt Chart Widget into SAC is a straightforward process. Once you have the `GanttChartWidget.json` file ready, you can upload the widget to SAC through the Custom Widgets panel. After uploading, the Gantt Chart Widget will be available for use in your dashboards. You can bind data to the widget and customize its properties to suit your needs.
Comments