top of page
Rechercher

SAP Analytics Cloud: Custom Table or Scorecard Widget with Multi-Select Function

Enter the Custom Table widget—a custom-built solution designed to supercharge SAC's analytical capabilities with a dynamic, tabular interface. This blog dives deep into the technical architecture, implementation details, and practical applications of this widget, which seamlessly integrates with both SAC import models and SAP BW, S4 live connections. 

This widget is released under the MIT License , which means you are free to use it. The code is completely self-contained and does not rely on any third-party libraries or external dependencies.

The widget’s core strength lies in its flexibility. It doesn’t care how many columns your dataset throws at it—it adjusts dynamically. Need to select one row or twenty? It’s got you covered with a toggle between single and multiple selection modes. Want to make your table pop with colors, symbols, or custom buttons? The styling panel makes it a breeze. Let’s explore how this is all stitched together, starting with the architecture.

Events and Methodes

The following events are accessible:

The following get methodes are available: 

The following set methodes are available: 

 

  1. onSelectionChanged - Fired whenever the user changes the row selection (either single or multiple).

  2. onCustomButtonClicked- Triggered when a dynamic button (e.g., “View Details”) is clicked, passing the button’s ID and configuration.

  3. onSelectionModeChange - Occurs when switching between single-select and multi-select modes.

  4. getSelectedRowData- Returns the selected rows’ data as a JSON string.

  5. getSelectedRows- Retrieves the indexes of selected rows as a JSON string.

  6. getSelectedRowsArray- Provides the selected row indexes as an array for easier processing.

  7. setSelectedRows-Sets the row selection using a JSON string of row indexes.

  8. getTableData / setTableData -These methods retrieve or update the table’s full dataset (in JSON format).

  9. getTableColumns / setTableColumns-Used to get or update the table’s column definitions dynamically.

  10. getMultiSelectMode / setMultiSelectMode-Returns or sets whether the widget is in multi-select mode (true/false).

  11. setSelectedDimensionFilter-Applies a filter based on a specific column (dimension) and its value.

  12. clearDimensionFilter / clearAllFilters-Clears a specific dimension filter or resets all active filters.

  13. getActiveDimensionFilter-Retrieves the current active filter for a given column.

  14. getFilteredRowCount-Returns the count of rows that match the current filter criteria.

  15. getButtonVisibility / setButtonVisibility-Gets or sets the visibility state (visible or hidden) of a dynamic button.

  16. getDynamicButtons-Returns the current dynamic button configuration as a JSON string.

  17. getLastClickedButtonId-Retrieves the ID of the last clicked dynamic button.


Customizability

In the Builder Panel, either a BW, S4 or Import Model type can be selected. One or more dimensions and measures can also be chosen. There is no fixed limit, and the table automatically adapts to the selected dimensions and measures. No further coding is necessary.

In the Styling Panel, you can customise the colours of the table and buttons, adjust the sizes of the displayed columns, or control the visibility of the buttons. It is also possible to replace a selected string with a symbol. Multiple rules can be defined for the same dimension or measure. For example, the word "in Stock" can be replaced with a check mark, and so on.




Search function: You can use one or more columns for the search functionality by clicking directly on the header.


Symbol replacement: You can apply one or more rules to replace string or measure values with symbols across one or multiple dimensions.




















Dynamic Buttons: As soon as the table is interacted with, you can work with the data by creating buttons, assigning IDs, and later using events to trigger SAC native functionalities. In the below example, we set the visability of the button and the button is called when clicked, we can also program SAC native functionality directly in the onCustomButtonClicked Script function.




 



System Architecture and Components

The widget comprises three main components:

  • Custom Widget JSON (planifyitTABv.json): This is the configuration hub, defining metadata, properties, methods, events, and data bindings. It tells SAC everything it needs to know about the widget’s capabilities.

  • Main Component (planifyit-tab-widget.js): The heart of the widget, this JavaScript file handles table rendering, user interactions, selection logic, and event dispatching. It’s where the magic of dynamic data display happens.

  • Styling Panel (style-panel.js): This component provides a design-time interface for customizing visuals—think colors, column widths, symbols, and buttons. It ensures the widget looks as good as it performs.

These components align with SAP’s Custom Widget Developer Guide, leveraging Web Components, Shadow DOM, and lifecycle methods to ensure isolation and compatibility. 

 

JSON Configuration

This JSON defines the widget’s metadata (id, name, vendor), references the main and styling components, and sets up properties like headerTitle and tableData. The methods section includes functions like getSelectedRows, while events like onSelectionChanged enable SAC to react to user actions. The dataBindings ensure seamless integration with SAC’s data feeds, supporting both dimensions and measures.

Example JSON Configuration

{"id": "planifyit_tab","version": "1.0.0","name": "PlanifyIT Table","description": "PlanifyIT Table Widget with Single and Multiple Selection","newInstancePrefix": "planifyit_tab","vendor": "Planifyit GmbH","license": "MIT","icon": "https://planifyit.github.io/PlanifyitTAB/PlanifyIT_Logo2.png","webcomponents": [{"kind": "main","tag": "planifyit-tab-widget","url": "https://planifyit.github.io/PlanifyitTAB/planifyit-tab-widget.js","integrity": "sha256-"},{"kind": "styling","tag": "com-planifyit-tab-styling","url": "https://planifyit.github.io/PlanifyitTAB/style-panel.js","integrity": "sha256-="}],"properties": {..."methods": {..."events": {..."dataBindings": {...

Main Component Implementation (planifyit-tab-widget.js)

This component handles rendering, selection logic, filtering, and dynamic UI elements. The _renderTable method, for example, dynamically generates the table based on tableData and tableColumns:

Rendering and Selection Logic Example

This code creates a header row with a checkbox for multiple selections (visible only in multi-select mode) and column headers with search icons. Clicking a header triggers a search field for filtering data.

_renderTable() {this._headerRow.innerHTML = `<th class="checkbox-column ${this._isMultiSelectMode ? 'show' : ''}"><input type="checkbox" id="selectAllCheckbox" class="select-checkbox"></th>`;this._tableColumns.forEach((col, colIndex) => {const th = document.createElement('th');const headerContainer = document.createElement('div');headerContainer.className = 'header-content';headerContainer.textContent = col.label || col.name;const searchIcon = document.createElement('span');searchIcon.className = 'search-icon';searchIcon.innerHTML = '';headerContainer.appendChild(searchIcon);th.appendChild(headerContainer);th.addEventListener('click', () => this._activateColumnSearch(colIndex, col));this._headerRow.appendChild(th);...

 

Selection Logic

The widget supports both single and multiple selections. Here’s how single-row selection is handled: 

_handleRowClick(index, e) {if (e.target.type === 'checkbox') return;if (!this._isMultiSelectMode) {this._selectedRows = [index];this._updateRowSelection();this._selectedRowsData = this._selectedRows.map(i => this._tableData[i]);this.dispatchEvent(new Event("onSelectionChanged"));this.dispatchEvent(new CustomEvent("propertiesChanged", {detail: {properties: {selectedRows: JSON.stringify(this._selectedRows),selectedRowsData: JSON.stringify(this._selectedRowsData)...

For multiple selections, checkboxes are used:

_handleCheckboxChange(index, e) {const isChecked = e.target.checked;if (isChecked) {if (!this._selectedRows.includes(index)) this._selectedRows.push(index);} else {this._selectedRows = this._selectedRows.filter(i => i !== index);}this._selectedRowsData = this._selectedRows.map(i => this._tableData[i]);this.dispatchEvent(new Event("onSelectionChanged"));...

 

Dynamic Buttons and Symbols

The widget allows users to define custom buttons:

_renderDynamicButtons() {const buttons = JSON.parse(this._dynamicButtons);buttons.forEach(buttonConfig => {if (buttonConfig.visibility !== 'hidden') {const button = document.createElement('button');button.className = 'dynamic-button';button.title = buttonConfig.tooltip || buttonConfig.id;button.textContent = this._symbolMap[buttonConfig.symbol] || '●';button.style.backgroundColor = buttonConfig.backgroundColor;button.addEventListener('click', () => {this._lastClickedButtonId = buttonConfig.id;this.dispatchEvent(new CustomEvent("onCustomButtonClicked", {detail: { buttonId: buttonConfig.id }....

Symbols replace text in cells based on mappings:

_getSymbols() {return [{ value: 'check', label: '✓ Check' },{ value: 'x', label: '✕ X' },{ value: 'arrow-up', label: '↑ Arrow Up' },..._buildSymbolMap() {const symbolMap = {};this._getSymbols().forEach(symbol => {....

Styling Panel Implementation (style-panel.js)

This component enables dynamic styling through user-configurable settings in SAC's design-time environment.

Example Code for Styling Panel

class StylePanel extends HTMLElement {constructor() {super();this._shadowRoot = this.attachShadow({ mode: "open" });this._shadowRoot.appendChild(template.content.cloneNode(true));this._headerColorInput = this._shadowRoot.getElementById("style_header_color");this._headerColorPicker = this._shadowRoot.getElementById("style_header_color_picker");this._headerColorPicker.addEventListener("input", () => {this._headerColorInput.value = this._headerColorPicker.value;});this._applyButton = this._shadowRoot.getElementById("apply_styles");this._applyButton.addEventListener("click", this._submit.bind(this));...
  

The panel lets users adjust colors, symbols, and buttons. For example, adding a symbol mapping:

_addMappingEntry(columnIndex = '', value = '', symbolType = 'circle') {const entry = document.createElement("div");entry.className = "mapping-entry";const columnInput = document.createElement("input");columnInput.type = "number";columnInput.value = columnIndex;const valueInput = document.createElement("input");valueInput.type = "text";valueInput.value = value;const symbolSelect = document.createElement("select");this._getSymbols().forEach(symbol => {const option = document.createElement("option");option.value = symbol.value;option.textContent = symbol.label;if (symbol.value === symbolType) option.selected = true;symbolSelect.appendChild(option);});entry.appendChild(columnInput);entry.appendChild(valueInput);entry.appendChild(symbolSelect);this._symbolMappingContainer.appendChild(entry);...

 

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 for the SAC Table Widget and upload it in your SAC tenant under Custom Widgets as follows:


 
 
 

Comments


bottom of page