18 KiB
SAP SAC Script API Reference for Custom Widgets
Comprehensive reference for Analytics Designer and Optimized Story Experience Script APIs relevant to custom widget development.
Sources:
- Analytics Designer API Reference Guide
- Optimized Story Experience API Reference Guide
- Custom Widget Developer Guide (PDF)
Note
: These documentation links point to the latest release version. Version-specific documentation may be available under versioned pages in the SAP Help Portal.
Table of Contents
- DataSource Object
- Selection Type
- MemberInfo Object
- ResultMemberInfo Object
- ResultSet APIs
- DataBinding Object
- Filter APIs
- Planning APIs
- Variable APIs
- Event Handling
DataSource Object
The DataSource object provides access to data model information and operations.
Getting DataSource
// In SAC Script
var ds = Table_1.getDataSource();
var ds = Chart_1.getDataSource();
// In custom widget (via script method)
Widget_1.getDataSource(); // If widget has data binding
DataSource Methods
getResultSet()
Returns result data based on optional parameters.
// Signature
getResultSet(options?: Object): Array<Object>
// Options object can include:
// - selection: Object - data selection context
// - offset: number - starting row index
// - limit: number - maximum rows to return
// Examples
var allData = ds.getResultSet();
var filteredData = ds.getResultSet({ selection: { "Year": "2024" } });
var pagedData = ds.getResultSet({ offset: 0, limit: 100 }); // First 100 rows
Return Value: Array of result objects containing:
- Dimension member info (id, description, parentId)
- Measure values (raw, formatted, unit)
getResultMember()
Returns member information for a specific selection.
// Signature
getResultMember(
dimensionId: string,
selection: Object
): Object | undefined
// Example
var memberInfo = ds.getResultMember("Account", { "Account": "Revenue" });
console.log(memberInfo.description); // "Revenue"
console.log(memberInfo.id); // "REVENUE"
getMembers()
Retrieves dimension members.
// Signature
getMembers(
dimensionId: string,
maxNumber?: number
): Array<Object>
// Examples
var allMembers = ds.getMembers("Account");
var limitedMembers = ds.getMembers("Account", 100); // Max 100 members
Note: Using getMembers() causes a backend roundtrip. For performance, prefer getResultSet() when possible as it doesn't require additional network calls.
getMember()
Returns information for a specific member.
// Signature
getMember(
dimensionId: string,
memberId: string
): Object
// Example
var member = ds.getMember("Account", "REVENUE");
getData()
Gets the raw data value for a selection.
// Signature
getData(selection?: Object): number | null
// Examples
var currentValue = ds.getData(); // Uses current selection context
var specificValue = ds.getData({
"Account": "Revenue",
"Year": "2024"
});
getDimensions()
Returns available dimensions in the data source.
// Signature
getDimensions(): Array<Object>
// Example
var dimensions = ds.getDimensions();
dimensions.forEach(dim => {
console.log(dim.id, dim.description);
});
getMeasures() / getMainStructureMembers()
Returns available measures.
// Signature
getMeasures(): Array<Object>
// Example
var measures = ds.getMeasures();
measures.forEach(m => {
console.log(m.id, m.description, m.unitOfMeasure);
});
Selection Type
Selection objects define data context for operations.
Selection Structure
// Simple selection
var selection = {
"Account": "Revenue",
"Year": "2024"
};
// Selection with multiple values
var multiSelection = {
"Account": ["Revenue", "Cost"],
"Year": "2024"
};
// Selection with hierarchy
var hierarchySelection = {
"Account": {
id: "Revenue",
hierarchyId: "H1"
}
};
Using Selection in JSON
{
"properties": {
"currentSelection": {
"type": "Selection",
"default": {},
"description": "Current data selection context"
}
},
"methods": {
"setSelection": {
"description": "Apply a data selection",
"parameters": [
{
"name": "selection",
"type": "Selection",
"description": "Selection to apply"
}
],
"body": "this._setSelection(selection);"
},
"getSelection": {
"description": "Get current selection",
"returnType": "Selection",
"body": "return this._getSelection();"
}
}
}
Selection in Web Component
class DataWidget extends HTMLElement {
constructor() {
super();
this._selection = {};
}
// Method called from SAC script
setSelection(selection) {
this._selection = selection;
this._render();
}
getSelection() {
return this._selection;
}
_render() {
// Use selection to filter/highlight data
console.log("Current selection:", this._selection);
}
}
MemberInfo Object
Represents dimension member information.
Structure
{
id: string, // Technical member ID (e.g., "REVENUE")
description: string, // Display name (e.g., "Revenue")
displayId: string, // Display ID
dimensionId: string, // Parent dimension ID
modelId: string, // Data model ID
parentId?: string, // Parent member ID (hierarchies)
hierarchyId?: string, // Hierarchy ID (if applicable)
level?: integer, // Hierarchy level
isNode?: boolean, // Is hierarchy node (has children)
properties?: object // Additional attributes
}
Performance Optimization
When using setDimensionFilter(), passing a MemberInfo object instead of just a member ID string prevents a backend roundtrip:
// Slower - causes roundtrip to fetch description
ds.setDimensionFilter("Account", "REVENUE");
// Faster - no roundtrip, MemberInfo already has description
ds.setDimensionFilter("Account", {
id: "REVENUE",
description: "Revenue"
});
Accessing Member Properties
// Get member with attributes
var member = ds.getMember("Product", "P001");
// Access properties (if dimension has attributes)
if (member.properties) {
console.log("Category:", member.properties.Category);
console.log("Brand:", member.properties.Brand);
}
ResultMemberInfo Object
Extended member information from result sets.
Structure
{
id: string, // Member ID
description: string, // Display name
parentId?: string, // Parent ID for hierarchies
formattedValue?: string, // Formatted display value
unitOfMeasure?: string, // Unit (for measures)
raw?: number, // Raw numeric value (for measures)
properties?: {
// Dimension attributes
[attributeName: string]: string
}
}
Accessing in Result Set
var resultSet = ds.getResultSet();
resultSet.forEach(row => {
// Access dimension member info
var accountMember = row["Account"];
console.log("Account ID:", accountMember.id);
console.log("Account Name:", accountMember.description);
// Access measure value
var revenue = row["Revenue"];
console.log("Value:", revenue.raw);
console.log("Formatted:", revenue.formattedValue);
console.log("Unit:", revenue.unitOfMeasure);
});
Note on Visibility
Only visible properties in the widget configuration are included in the ResultMemberInfo object. Hidden dimensions/measures won't appear.
ResultSet APIs
getResultSet() Deep Dive
// Full signature
getResultSet(
selection?: Selection | Selection[] | SelectionContext,
offset?: integer,
limit?: integer
): ResultSet[]
Pagination
// Get first page (100 rows)
var page1 = ds.getResultSet(null, 0, 100);
// Get second page
var page2 = ds.getResultSet(null, 100, 100);
// Count total rows
var total = ds.getResultSetCount();
Filtering
// Single filter
var filtered = ds.getResultSet({ "Year": "2024" });
// Multiple filters
var filtered = ds.getResultSet({
"Year": "2024",
"Region": "EMEA"
});
// Array of selections (OR logic)
var filtered = ds.getResultSet([
{ "Year": "2024" },
{ "Year": "2023" }
]);
Processing Result Sets
function processResults(resultSet) {
var chartData = [];
resultSet.forEach(row => {
// Dynamically access columns based on data binding
var dimensions = Object.keys(row).filter(k => row[k].id !== undefined);
var measures = Object.keys(row).filter(k => row[k].raw !== undefined);
chartData.push({
category: row[dimensions[0]]?.description || "Unknown",
value: row[measures[0]]?.raw || 0
});
});
return chartData;
}
DataBinding Object
Custom widget data binding API.
Getting DataBinding
// In custom widget web component
const binding = this.dataBindings.getDataBinding("myDataBinding");
DataBinding Methods
getResultSet()
// Async - returns Promise
const resultSet = await binding.getResultSet();
getMembers()
// Get dimension members
const members = await binding.getMembers("DimensionId");
addDimensionToFeed()
// Programmatically add dimension to feed
await binding.addDimensionToFeed("dimensions", "Account");
addMeasureToFeed()
// Add measure to feed
await binding.addMeasureToFeed("measures", "Revenue");
removeDimensionFromFeed()
await binding.removeDimensionFromFeed("dimensions", "Account");
Accessing Bound Data Directly
// Direct property access (name from JSON dataBindings)
const data = this.myDataBinding;
// Structure
{
data: ResultSet[], // Array of result rows
metadata: {
dimensions: {}, // Dimension info
mainStructureMembers: {} // Measure info
}
}
Data Binding Example
class DataBoundWidget extends HTMLElement {
onCustomWidgetAfterUpdate(changedProperties) {
this._processData();
}
_processData() {
// Access data binding by name defined in JSON
const binding = this.chartData;
if (!binding || !binding.data) {
this._showNoData();
return;
}
// Process rows
const chartData = binding.data.map(row => {
// Access dimension (first feed)
const categoryKey = Object.keys(row).find(k =>
binding.metadata.dimensions[k]);
const valueKey = Object.keys(row).find(k =>
binding.metadata.mainStructureMembers[k]);
return {
label: row[categoryKey]?.description || "",
value: row[valueKey]?.raw || 0
};
});
this._renderChart(chartData);
}
}
Filter APIs
setDimensionFilter()
// Set single filter
ds.setDimensionFilter("Year", "2024");
// Set filter with MemberInfo (avoids roundtrip)
ds.setDimensionFilter("Year", {
id: "2024",
description: "Year 2024"
});
// Multiple values
ds.setDimensionFilter("Year", ["2023", "2024"]);
// Clear filter
ds.removeDimensionFilter("Year");
setVariableValue()
// Set planning variable
ds.setVariableValue("VAR_YEAR", "2024");
// Multiple values
ds.setVariableValue("VAR_REGION", ["EMEA", "AMER"]);
Filter Synchronization
// Apply filters and refresh data
ds.setDimensionFilter("Year", "2024");
ds.setDimensionFilter("Region", "EMEA");
// Refresh to apply (may be automatic depending on widget)
ds.refresh();
Planning APIs
For widgets supporting SAP Analytics Cloud Planning.
⚠️ Important: Planning APIs are synchronous and return boolean success values. Always check the return value to handle errors appropriately.
Write-back Methods
// Set user input (planning)
ds.setUserInput(selection, value);
// Example
ds.setUserInput({
"Account": "Forecast",
"Year": "2025",
"Month": "Jan"
}, 100000);
// Submit changes (synchronous, returns boolean)
var success = ds.submitData();
if (!success) {
console.error("Submit failed");
}
// Revert changes using Planning Version
var planningVersion = ds.getPlanningVersion();
planningVersion.revert();
Planning Workflow
class PlanningWidget extends HTMLElement {
saveData(entries) {
const ds = this._dataSource;
// Apply all inputs
for (const entry of entries) {
ds.setUserInput(entry.selection, entry.value);
}
// Submit to backend (synchronous)
var success = ds.submitData();
if (success) {
this._showSuccess("Data saved");
} else {
// Rollback on error using Planning Version
var planningVersion = ds.getPlanningVersion();
if (planningVersion) {
planningVersion.revert();
}
this._showError("Save failed");
}
}
}
Data Locking
// Get data locking interface
var dataLocking = ds.getDataLocking();
// Check lock status
var isLocked = dataLocking.isLocked();
// Set lock state (returns boolean)
var lockSuccess = dataLocking.setState(true); // Lock
if (!lockSuccess) {
console.error("Failed to acquire lock");
}
// Release lock
var unlockSuccess = dataLocking.setState(false); // Unlock
if (!unlockSuccess) {
console.error("Failed to release lock");
}
Variable APIs
Input Variable Methods
// Get variable value
var year = ds.getVariableValue("VAR_YEAR");
// Set variable value
ds.setVariableValue("VAR_YEAR", "2024");
// Get available values
var values = ds.getVariableValues("VAR_YEAR");
Variable Information
// Get variable details
var varInfo = ds.getVariable("VAR_YEAR");
console.log(varInfo.description); // "Fiscal Year"
console.log(varInfo.mandatory); // true/false
console.log(varInfo.multiValue); // true/false
Event Handling
Widget Events in JSON
{
"events": {
"onSelect": {
"description": "Fired when item is selected"
},
"onDataChange": {
"description": "Fired when data changes"
},
"onError": {
"description": "Fired on error"
}
}
}
Firing Events
// Simple event
this.dispatchEvent(new Event("onSelect"));
// Event with data
this.dispatchEvent(new CustomEvent("onSelect", {
detail: {
selection: this._currentSelection,
value: this._selectedValue
}
}));
// Error event
this.dispatchEvent(new CustomEvent("onError", {
detail: {
message: "Failed to load data",
code: "DATA_ERROR"
}
}));
Handling in SAC Script
// Event handler in SAC script
Widget_1.onSelect = function(event) {
var info = Widget_1.getEventInfo();
console.log("Selected:", info.selection);
console.log("Value:", info.value);
// Update other widgets
Table_1.getDataSource().setDimensionFilter("Account", info.selection.Account);
};
Widget_1.onError = function(event) {
var info = Widget_1.getEventInfo();
Application.showMessage(ApplicationMessageType.Error, info.message);
};
Data-Driven Events
class InteractiveWidget extends HTMLElement {
_handleClick(item) {
// Build selection from clicked item
const selection = {
[this._dimensionId]: item.id
};
// Store for getEventInfo
this._lastEvent = {
selection: selection,
label: item.label,
value: item.value
};
// Fire event
this.dispatchEvent(new Event("onSelect"));
}
// Called by SAC script via getEventInfo()
getEventInfo() {
return this._lastEvent || {};
}
}
Common Patterns
Async Data Loading
class AsyncWidget extends HTMLElement {
async connectedCallback() {
this._showLoading();
try {
const binding = this.dataBindings.getDataBinding("myData");
const resultSet = await binding.getResultSet();
this._renderData(resultSet);
} catch (error) {
this._showError(error.message);
}
}
_showLoading() {
this._shadowRoot.innerHTML = '<div class="loading">Loading...</div>';
}
}
Selection Synchronization
class SyncWidget extends HTMLElement {
// Apply external selection
setSelection(selection) {
this._selection = selection;
this._highlightSelection();
}
// Notify of internal selection
_onItemClick(item) {
this._selection = { [this._dimId]: item.id };
this.dispatchEvent(new CustomEvent("propertiesChanged", {
detail: { properties: { currentSelection: this._selection } }
}));
this.dispatchEvent(new Event("onSelect"));
}
}
Resources
- Analytics Designer API Reference Guide
- Optimized Story Experience API Reference Guide
- Custom Widget Developer Guide (PDF)
- Use Result Set APIs
- SAP Help Portal - Custom Widgets
Last Updated: 2025-11-22