Files
gh-secondsky-sap-skills-ski…/references/script-api-reference.md
2025-11-30 08:55:27 +08:00

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:

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

  1. DataSource Object
  2. Selection Type
  3. MemberInfo Object
  4. ResultMemberInfo Object
  5. ResultSet APIs
  6. DataBinding Object
  7. Filter APIs
  8. Planning APIs
  9. Variable APIs
  10. 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


Last Updated: 2025-11-22