832 lines
23 KiB
JavaScript
832 lines
23 KiB
JavaScript
/**
|
|
* SAP Analytics Cloud - Common Scripting Patterns
|
|
*
|
|
* Ready-to-use code patterns for SAC Analytics Designer and Optimized Story Experience.
|
|
* Copy and adapt these patterns to your application.
|
|
*
|
|
* Source: SAP Analytics Cloud Scripting Skill
|
|
* Version: 2025.14+
|
|
*/
|
|
|
|
// =============================================================================
|
|
// FILTERING PATTERNS
|
|
// =============================================================================
|
|
|
|
/**
|
|
* Pattern 1: Apply filter from chart selection to table
|
|
* Use in: Chart.onSelect event
|
|
*/
|
|
function filterTableFromChartSelection() {
|
|
var selections = Chart_1.getSelections();
|
|
if (selections.length > 0) {
|
|
var selectedMember = selections[0]["{{DimensionId}}"];
|
|
Table_1.getDataSource().setDimensionFilter("{{DimensionId}}", selectedMember);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Pattern 2: Apply multiple filters efficiently (batch)
|
|
* Use when: Setting multiple filters at once
|
|
*/
|
|
function applyMultipleFilters(year, region, product) {
|
|
var ds = Chart_1.getDataSource();
|
|
|
|
// Pause to prevent multiple refreshes
|
|
ds.setRefreshPaused(true);
|
|
|
|
ds.setDimensionFilter("Year", year);
|
|
ds.setDimensionFilter("Region", region);
|
|
ds.setDimensionFilter("Product", product);
|
|
|
|
// Single refresh
|
|
ds.setRefreshPaused(false);
|
|
}
|
|
|
|
/**
|
|
* Pattern 3: Sync filters across multiple widgets
|
|
* Use when: Multiple charts/tables should have same filters
|
|
*/
|
|
function syncFiltersAcrossWidgets() {
|
|
var sourceDs = Chart_1.getDataSource();
|
|
|
|
// Copy all filters efficiently
|
|
Table_1.getDataSource().copyDimensionFilterFrom(sourceDs);
|
|
Chart_2.getDataSource().copyDimensionFilterFrom(sourceDs);
|
|
}
|
|
|
|
/**
|
|
* Pattern 4: Reset all filters
|
|
* Use in: Reset button onClick event
|
|
*/
|
|
function resetAllFilters() {
|
|
var widgets = [Chart_1, Table_1, Chart_2];
|
|
|
|
widgets.forEach(function(widget) {
|
|
widget.getDataSource().clearAllFilters();
|
|
});
|
|
|
|
// Reset dropdowns
|
|
Dropdown_Year.setSelectedKey("");
|
|
Dropdown_Region.setSelectedKey("");
|
|
}
|
|
|
|
/**
|
|
* Pattern 5: Filter from dropdown selection
|
|
* Use in: Dropdown.onSelect event
|
|
*/
|
|
function filterFromDropdown() {
|
|
var selectedYear = Dropdown_Year.getSelectedKey();
|
|
|
|
if (selectedYear) {
|
|
Application.showBusyIndicator();
|
|
|
|
Chart_1.getDataSource().setDimensionFilter("Year", selectedYear);
|
|
Table_1.getDataSource().setDimensionFilter("Year", selectedYear);
|
|
|
|
Application.hideBusyIndicator();
|
|
}
|
|
}
|
|
|
|
|
|
// =============================================================================
|
|
// DATA ACCESS PATTERNS
|
|
// =============================================================================
|
|
|
|
/**
|
|
* Pattern 6: Find active version from attribute
|
|
* Use when: Need to identify active planning cycle/version from master data
|
|
*/
|
|
function findActiveVersion() {
|
|
var allVersions = PlanningModel_1.getMembers("Version");
|
|
var activeVersion = null;
|
|
|
|
for (var i = 0; i < allVersions.length; i++) {
|
|
if (allVersions[i].properties.Active === "X") {
|
|
activeVersion = allVersions[i].id;
|
|
break;
|
|
}
|
|
}
|
|
|
|
console.log("Active Version: " + activeVersion);
|
|
return activeVersion;
|
|
}
|
|
|
|
/**
|
|
* Pattern 7: Get booked values only (not all master data)
|
|
* Use when: Need only members that have data, not entire master data list
|
|
*/
|
|
function getBookedValuesOnly() {
|
|
var bookedMembers = Table_1.getDataSource().getMembers(
|
|
"{{DimensionId}}",
|
|
{ accessMode: MemberAccessMode.BookedValues }
|
|
);
|
|
|
|
console.log("Booked members:", bookedMembers);
|
|
return bookedMembers;
|
|
}
|
|
|
|
/**
|
|
* Pattern 8: Get data value for specific selection
|
|
* Use when: Need to read specific cell value
|
|
*/
|
|
function getSpecificDataValue() {
|
|
var selection = {
|
|
"@MeasureDimension": "[Account].[parentId].&[Revenue]",
|
|
"Location": "[Location].[Country].&[US]",
|
|
"Year": "[Date].[Year].&[2024]"
|
|
};
|
|
|
|
var data = Chart_1.getDataSource().getData(selection);
|
|
|
|
console.log("Formatted:", data.formattedValue);
|
|
console.log("Raw:", data.rawValue);
|
|
|
|
return data;
|
|
}
|
|
|
|
|
|
// =============================================================================
|
|
// CHART MANIPULATION PATTERNS
|
|
// =============================================================================
|
|
|
|
/**
|
|
* Pattern 9: Dynamic measure swap
|
|
* Use in: Button onClick or Dropdown onSelect
|
|
*/
|
|
function swapMeasure(newMeasureId) {
|
|
// Get current measure
|
|
var currentMeasures = Chart_1.getMembers(Feed.ValueAxis);
|
|
|
|
// Remove current measure
|
|
if (currentMeasures.length > 0) {
|
|
Chart_1.removeMember(Feed.ValueAxis, currentMeasures[0]);
|
|
}
|
|
|
|
// Add new measure
|
|
Chart_1.addMember(Feed.ValueAxis, newMeasureId);
|
|
}
|
|
|
|
/**
|
|
* Pattern 10: Dynamic dimension swap
|
|
* Use when: Changing chart axis dimension
|
|
*/
|
|
function swapDimension(newDimensionId) {
|
|
// Get current dimension
|
|
var currentDimensions = Chart_1.getMembers(Feed.CategoryAxis);
|
|
|
|
// Remove current
|
|
if (currentDimensions.length > 0) {
|
|
Chart_1.removeDimension(Feed.CategoryAxis, currentDimensions[0]);
|
|
}
|
|
|
|
// Add new
|
|
Chart_1.addDimension(Feed.CategoryAxis, newDimensionId);
|
|
}
|
|
|
|
/**
|
|
* Pattern 11: Top N ranking
|
|
* Use when: Show only top performers
|
|
*/
|
|
function showTopN(n, measureId) {
|
|
Chart_1.rankBy(measureId, n, SortOrder.Descending);
|
|
}
|
|
|
|
|
|
// =============================================================================
|
|
// VISIBILITY TOGGLE PATTERNS
|
|
// =============================================================================
|
|
|
|
/**
|
|
* Pattern 12: Switch between chart and table
|
|
* Use in: Toggle button onClick event
|
|
*/
|
|
function switchChartTable(showChart) {
|
|
Chart_1.setVisible(showChart);
|
|
Table_1.setVisible(!showChart);
|
|
|
|
Button_ShowChart.setVisible(!showChart);
|
|
Button_ShowTable.setVisible(showChart);
|
|
}
|
|
|
|
/**
|
|
* Pattern 13: Expandable panel pattern
|
|
* Use when: Toggle detail section visibility
|
|
*/
|
|
function toggleDetailPanel() {
|
|
var isVisible = Panel_Details.isVisible();
|
|
Panel_Details.setVisible(!isVisible);
|
|
|
|
// Update button text
|
|
if (isVisible) {
|
|
Button_Toggle.setText("Show Details");
|
|
} else {
|
|
Button_Toggle.setText("Hide Details");
|
|
}
|
|
}
|
|
|
|
|
|
// =============================================================================
|
|
// USER FEEDBACK PATTERNS
|
|
// =============================================================================
|
|
|
|
/**
|
|
* Pattern 14: Long operation with busy indicator
|
|
* Use when: Performing operations that take time
|
|
*/
|
|
function performLongOperation() {
|
|
Application.showBusyIndicator();
|
|
|
|
try {
|
|
// Perform operation
|
|
Table_1.getDataSource().refreshData();
|
|
Chart_1.getDataSource().refreshData();
|
|
|
|
Application.showMessage(
|
|
ApplicationMessageType.Success,
|
|
"Data refreshed successfully"
|
|
);
|
|
} catch (error) {
|
|
console.log("Error:", error);
|
|
Application.showMessage(
|
|
ApplicationMessageType.Error,
|
|
"Failed to refresh data"
|
|
);
|
|
} finally {
|
|
Application.hideBusyIndicator();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Pattern 15: Confirmation popup pattern
|
|
* Use when: Confirming destructive actions
|
|
*/
|
|
// In main button onClick:
|
|
function showConfirmation() {
|
|
Popup_Confirm.open();
|
|
}
|
|
|
|
// In popup Yes button onClick:
|
|
function onConfirmYes() {
|
|
Popup_Confirm.close();
|
|
performAction();
|
|
}
|
|
|
|
// In popup No button onClick:
|
|
function onConfirmNo() {
|
|
Popup_Confirm.close();
|
|
}
|
|
|
|
|
|
// =============================================================================
|
|
// SELECTION HANDLING PATTERNS
|
|
// =============================================================================
|
|
|
|
/**
|
|
* Pattern 16: Handle multi-selection from table
|
|
* Use in: Table.onSelect event
|
|
*/
|
|
function handleMultiSelection() {
|
|
var selections = Table_1.getSelections();
|
|
|
|
if (selections.length === 0) {
|
|
Application.showMessage(
|
|
ApplicationMessageType.Warning,
|
|
"Please select at least one row"
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Process all selections
|
|
var selectedIds = selections.map(function(sel) {
|
|
return sel["{{DimensionId}}"];
|
|
});
|
|
|
|
console.log("Selected IDs:", selectedIds);
|
|
|
|
// Apply as filter (if single value needed)
|
|
if (selections.length === 1) {
|
|
Chart_1.getDataSource().setDimensionFilter(
|
|
"{{DimensionId}}",
|
|
selectedIds[0]
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Pattern 17: Clear selection
|
|
* Use when: Need to programmatically clear widget selection
|
|
*/
|
|
function clearSelection() {
|
|
Chart_1.getDataSource().removeDimensionFilter("{{DimensionId}}");
|
|
Table_1.getDataSource().removeDimensionFilter("{{DimensionId}}");
|
|
}
|
|
|
|
|
|
// =============================================================================
|
|
// INITIALIZATION PATTERNS
|
|
// =============================================================================
|
|
|
|
/**
|
|
* Pattern 18: Set initial filters from URL parameters
|
|
* Prerequisites:
|
|
* - Create global variable with p_ prefix (e.g., p_year)
|
|
* - URL: ?p_year=2024&p_region=EMEA
|
|
*/
|
|
// In Script Object (not onInitialization):
|
|
function applyUrlParameters() {
|
|
if (p_year) {
|
|
Chart_1.getDataSource().setDimensionFilter("Year", p_year);
|
|
Dropdown_Year.setSelectedKey(p_year);
|
|
}
|
|
|
|
if (p_region) {
|
|
Chart_1.getDataSource().setDimensionFilter("Region", p_region);
|
|
Dropdown_Region.setSelectedKey(p_region);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Pattern 19: Dynamic title update
|
|
* Use when: Title should reflect current filters
|
|
*/
|
|
function updateDynamicTitle() {
|
|
var year = Dropdown_Year.getSelectedKey() || "All Years";
|
|
var region = Dropdown_Region.getSelectedKey() || "All Regions";
|
|
|
|
Text_Title.setText("Sales Report - " + year + " | " + region);
|
|
}
|
|
|
|
|
|
// =============================================================================
|
|
// DEBUGGING PATTERNS
|
|
// =============================================================================
|
|
|
|
/**
|
|
* Pattern 20: Debug logging helper
|
|
* Use during development
|
|
*/
|
|
function debugLog(label, value) {
|
|
console.log("=== " + label + " ===");
|
|
console.log(value);
|
|
console.log("Type:", typeof value);
|
|
if (Array.isArray(value)) {
|
|
console.log("Length:", value.length);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Pattern 21: Log all selections
|
|
* Use when debugging selection handling
|
|
*/
|
|
function logSelections(widgetName, widget) {
|
|
var selections = widget.getSelections();
|
|
console.log(widgetName + " selections:");
|
|
console.log(JSON.stringify(selections));
|
|
}
|
|
|
|
|
|
// =============================================================================
|
|
// EXPORT PATTERNS
|
|
// =============================================================================
|
|
|
|
/**
|
|
* Pattern 22: Export with dynamic filename
|
|
* Use in: Export button onClick
|
|
*/
|
|
function exportWithDynamicName() {
|
|
var today = new Date().toISOString().split('T')[0];
|
|
var year = Dropdown_Year.getSelectedKey() || "AllYears";
|
|
|
|
Table_1.export(ExportType.Excel, {
|
|
fileName: "SalesReport_" + year + "_" + today
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Pattern 23: PDF export with header/footer
|
|
* Use in: Export PDF button onClick
|
|
*/
|
|
function exportPDF() {
|
|
var year = Dropdown_Year.getSelectedKey() || "All Years";
|
|
var today = new Date().toLocaleDateString();
|
|
|
|
Application.export(ExportType.PDF, {
|
|
scope: ExportScope.All,
|
|
header: "Sales Analysis Report - " + year,
|
|
footer: "Generated: " + today + " | Page {page}",
|
|
orientation: "landscape"
|
|
});
|
|
}
|
|
|
|
|
|
// =============================================================================
|
|
// TYPE CONVERSION PATTERNS (SAC requires explicit conversion)
|
|
// =============================================================================
|
|
|
|
/**
|
|
* Pattern 24: Explicit type conversion (required in SAC)
|
|
* Use when: Concatenating strings with numbers
|
|
*/
|
|
function typeConversionExamples() {
|
|
var myNumber = 42;
|
|
var myDecimal = 3.14;
|
|
var myBoolean = true;
|
|
|
|
// WRONG: Auto-conversion not allowed
|
|
// console.log("Value: " + myNumber);
|
|
|
|
// CORRECT: Explicit conversion
|
|
console.log("Integer: " + myNumber.toString());
|
|
console.log("Decimal: " + myDecimal.toString());
|
|
console.log("Boolean: " + myBoolean.toString());
|
|
|
|
// Using ConvertUtils
|
|
var numStr = ConvertUtils.numberToString(myNumber);
|
|
var strNum = ConvertUtils.stringToInteger("42");
|
|
}
|
|
|
|
/**
|
|
* Pattern 25: String formatting utilities
|
|
* Use when: Formatting data for display
|
|
*/
|
|
function stringFormatting() {
|
|
var value = 1234.567;
|
|
|
|
// Fixed decimal places
|
|
var fixed = value.toFixed(2); // "1234.57"
|
|
|
|
// Format number (use NumberFormat)
|
|
var formatted = NumberFormat.format(value, "#,##0.00");
|
|
|
|
// Date formatting
|
|
var now = new Date();
|
|
var dateStr = DateFormat.format(now, "yyyy-MM-dd");
|
|
var timeStr = DateFormat.format(now, "HH:mm:ss");
|
|
}
|
|
|
|
|
|
// =============================================================================
|
|
// LOOP PATTERNS (SAC-specific requirements)
|
|
// =============================================================================
|
|
|
|
/**
|
|
* Pattern 26: For loop with explicit iterator declaration
|
|
* IMPORTANT: SAC requires 'var' declaration for loop iterator
|
|
*/
|
|
function forLoopPattern() {
|
|
var members = Table_1.getDataSource().getMembers("Location", 10);
|
|
|
|
// CORRECT: Iterator declared with var
|
|
for (var i = 0; i < members.length; i++) {
|
|
console.log(members[i].id + ": " + members[i].description);
|
|
}
|
|
|
|
// WRONG: Iterator not declared (will cause error)
|
|
// for (i = 0; i < members.length; i++) { ... }
|
|
}
|
|
|
|
/**
|
|
* Pattern 27: While loop pattern
|
|
* Use when: Iterating until condition met
|
|
*/
|
|
function whileLoopPattern() {
|
|
var count = 0;
|
|
var maxIterations = 10;
|
|
|
|
while (count < maxIterations) {
|
|
console.log("Iteration: " + count.toString());
|
|
count++;
|
|
|
|
// Safety break condition
|
|
if (count > 100) {
|
|
console.log("Safety break triggered");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Pattern 28: For-in loop for selections
|
|
* Use when: Iterating over selection object properties
|
|
*/
|
|
function forInLoopPattern() {
|
|
var selection = Table_1.getSelections()[0];
|
|
|
|
if (selection) {
|
|
console.log("Selection properties:");
|
|
for (var propKey in selection) {
|
|
var propValue = selection[propKey];
|
|
console.log(" " + propKey + ": " + propValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// =============================================================================
|
|
// ARRAY PATTERNS
|
|
// =============================================================================
|
|
|
|
/**
|
|
* Pattern 29: Create typed 1D array
|
|
* Use when: Creating arrays for API calls
|
|
*/
|
|
function create1DArray() {
|
|
// Using ArrayUtils for typed arrays
|
|
var numberArray = ArrayUtils.create(Type.number);
|
|
numberArray.push(1);
|
|
numberArray.push(2);
|
|
numberArray.push(3);
|
|
|
|
var stringArray = ArrayUtils.create(Type.string);
|
|
stringArray.push("A");
|
|
stringArray.push("B");
|
|
|
|
// Literal syntax also works
|
|
var simpleArray = [1, 2, 3];
|
|
}
|
|
|
|
/**
|
|
* Pattern 30: Create 2D array (matrix)
|
|
* Use when: Building tabular data structures
|
|
*/
|
|
function create2DArray() {
|
|
var numCols = 4;
|
|
var numRows = 3;
|
|
|
|
// Create first row (required for type inference)
|
|
var firstRow = ArrayUtils.create(Type.number);
|
|
var arr2D = [firstRow];
|
|
|
|
// Add remaining rows
|
|
for (var i = 1; i < numRows; i++) {
|
|
arr2D.push(ArrayUtils.create(Type.number));
|
|
}
|
|
|
|
// Fill with values
|
|
var count = 0;
|
|
for (var row = 0; row < numRows; row++) {
|
|
for (var col = 0; col < numCols; col++) {
|
|
arr2D[row][col] = count;
|
|
count++;
|
|
}
|
|
}
|
|
|
|
// Access values
|
|
var value = arr2D[1][2]; // Row 1, Column 2
|
|
console.log("Value at [1][2]: " + value.toString());
|
|
}
|
|
|
|
|
|
// =============================================================================
|
|
// ADVANCED FILTER PATTERNS
|
|
// =============================================================================
|
|
|
|
/**
|
|
* Pattern 31: Exclude filter (filter OUT specific values)
|
|
* Use when: Removing specific members from drill-down
|
|
*/
|
|
function excludeFilterPattern() {
|
|
// Exclude single value
|
|
DS_1.setDimensionFilter("EMPLOYEE_ID", {
|
|
value: "230",
|
|
exclude: true
|
|
});
|
|
|
|
// Exclude multiple values
|
|
DS_1.setDimensionFilter("EMPLOYEE_ID", {
|
|
values: ["230", "240", "250"],
|
|
exclude: true
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Pattern 32: Range filter (numeric dimensions only)
|
|
* Use when: Filtering by numeric ranges
|
|
* NOTE: Not supported for time dimensions or SAP BW
|
|
*/
|
|
function rangeFilterPattern() {
|
|
// Between range
|
|
DS_1.setDimensionFilter("EMPLOYEE_ID", {
|
|
from: "100",
|
|
to: "500"
|
|
});
|
|
|
|
// Less than
|
|
DS_1.setDimensionFilter("EMPLOYEE_ID", {
|
|
less: "100"
|
|
});
|
|
|
|
// Greater than or equal
|
|
DS_1.setDimensionFilter("EMPLOYEE_ID", {
|
|
greaterOrEqual: "500"
|
|
});
|
|
|
|
// Multiple ranges (OR logic)
|
|
DS_1.setDimensionFilter("EMPLOYEE_ID", [
|
|
{ less: "100" },
|
|
{ greater: "900" }
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Pattern 33: Get and process dimension filters
|
|
* Use when: Need to read current filter state
|
|
*/
|
|
function getFilterState() {
|
|
var filters = Table_1.getDataSource().getDimensionFilters("COUNTRY");
|
|
|
|
for (var i = 0; i < filters.length; i++) {
|
|
var filter = filters[i];
|
|
|
|
switch (filter.type) {
|
|
case FilterValueType.Single:
|
|
var single = cast(Type.SingleFilterValue, filter);
|
|
console.log("Single filter: " + single.value);
|
|
break;
|
|
|
|
case FilterValueType.Multiple:
|
|
var multi = cast(Type.MultipleFilterValue, filter);
|
|
console.log("Multiple filter: " + multi.values.join(", "));
|
|
break;
|
|
|
|
case FilterValueType.Range:
|
|
var range = cast(Type.RangeFilterValue, filter);
|
|
if (range.from) {
|
|
console.log("Range: " + range.from + " to " + range.to);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// =============================================================================
|
|
// HIERARCHY PATTERNS
|
|
// =============================================================================
|
|
|
|
/**
|
|
* Pattern 34: Set hierarchy drill level
|
|
* Use when: Controlling hierarchy expansion
|
|
*/
|
|
function setHierarchyLevel() {
|
|
var ds = Table_1.getDataSource();
|
|
|
|
// Set active hierarchy
|
|
ds.setHierarchy("Location", "State_Hierarchy");
|
|
|
|
// Set drill level (0 = collapsed, higher = more expanded)
|
|
ds.setHierarchyLevel("Location", 2);
|
|
|
|
// Get current level
|
|
var currentLevel = ds.getHierarchyLevel("Location");
|
|
console.log("Current level: " + currentLevel.toString());
|
|
}
|
|
|
|
/**
|
|
* Pattern 35: Expand/collapse hierarchy nodes
|
|
* Use when: Programmatic hierarchy manipulation
|
|
*/
|
|
function expandCollapseNode() {
|
|
var ds = Chart_1.getDataSource();
|
|
|
|
// Expand specific node
|
|
ds.expandNode("Location", {
|
|
"Location": "[Location].[State_Hierarchy].&[California]",
|
|
"@MeasureDimension": "[Account].[parentId].&[Revenue]"
|
|
});
|
|
|
|
// Collapse node
|
|
ds.collapseNode("Location", {
|
|
"Location": "[Location].[State_Hierarchy].&[California]",
|
|
"@MeasureDimension": "[Account].[parentId].&[Revenue]"
|
|
});
|
|
}
|
|
|
|
|
|
// =============================================================================
|
|
// R VISUALIZATION PATTERNS
|
|
// =============================================================================
|
|
|
|
/**
|
|
* Pattern 36: Read R environment variable from JavaScript
|
|
* Use when: Getting calculated values from R script
|
|
*/
|
|
function readFromREnvironment() {
|
|
// R script created: gmCorrelation <- cor(grossMargin, grossMarginPlan)
|
|
|
|
var correlation = RVisualization_1
|
|
.getEnvironmentValues()
|
|
.getNumber("gmCorrelation");
|
|
|
|
console.log("Correlation coefficient: " + correlation.toString());
|
|
|
|
// Update text widget with R result
|
|
Text_Correlation.setText("Correlation: " + correlation.toFixed(3));
|
|
}
|
|
|
|
/**
|
|
* Pattern 37: Write to R environment from JavaScript
|
|
* Use when: Passing user input to R script
|
|
*/
|
|
function writeToREnvironment() {
|
|
// Get user selection
|
|
var userSelection = Dropdown_1.getSelectedKey();
|
|
var numValue = ConvertUtils.stringToInteger(userSelection);
|
|
|
|
// Pass to R environment
|
|
RVisualization_1
|
|
.getInputParameters()
|
|
.setNumber("userSelection", numValue);
|
|
|
|
// R script can now use: userSelection variable
|
|
}
|
|
|
|
|
|
// =============================================================================
|
|
// METHOD CHAINING PATTERNS
|
|
// =============================================================================
|
|
|
|
/**
|
|
* Pattern 38: Method chaining for concise code
|
|
* Use when: One-time data access, debugging
|
|
*/
|
|
function methodChainingExamples() {
|
|
// Chained logging (no intermediate variables)
|
|
console.log(Chart_1.getDataSource().getVariables());
|
|
|
|
// Chained filter application
|
|
Chart_1.getDataSource().setDimensionFilter("Year", "2024");
|
|
|
|
// Chained member access
|
|
var firstMember = Table_1.getDataSource()
|
|
.getMembers("Location", 1)[0];
|
|
|
|
// When NOT to chain: need to reuse result
|
|
var ds = Chart_1.getDataSource(); // Reuse ds
|
|
ds.setDimensionFilter("Year", "2024");
|
|
ds.setDimensionFilter("Region", "EMEA");
|
|
ds.refreshData();
|
|
}
|
|
|
|
|
|
// =============================================================================
|
|
// EQUALITY COMPARISON PATTERNS
|
|
// =============================================================================
|
|
|
|
/**
|
|
* Pattern 39: Using strict equality (recommended)
|
|
* SAC supports === always, == only with same types
|
|
*/
|
|
function equalityComparison() {
|
|
var aNumber = 1;
|
|
var aString = "1";
|
|
|
|
// CORRECT: Strict equality (always works)
|
|
if (aNumber === 1) {
|
|
console.log("Number equals 1");
|
|
}
|
|
|
|
if (aString === "1") {
|
|
console.log("String equals '1'");
|
|
}
|
|
|
|
// CORRECT: Compare same types
|
|
if (aString == "1") {
|
|
console.log("String comparison works");
|
|
}
|
|
|
|
// WOULD ERROR: Different types with ==
|
|
// if (aNumber == "1") { } // Don't do this!
|
|
}
|
|
|
|
|
|
// =============================================================================
|
|
// DATASOURCE INFO PATTERNS
|
|
// =============================================================================
|
|
|
|
/**
|
|
* Pattern 40: Get data source metadata
|
|
* Use when: Need model information for logging or display
|
|
*/
|
|
function getDataSourceInfo() {
|
|
var dsInfo = Table_1.getDataSource().getInfo();
|
|
|
|
console.log("Model: " + dsInfo.modelName);
|
|
console.log("Model ID: " + dsInfo.modelId);
|
|
console.log("Description: " + dsInfo.modelDescription);
|
|
|
|
// SAP BW only properties
|
|
if (dsInfo.sourceName !== undefined) {
|
|
console.log("Source: " + dsInfo.sourceName);
|
|
console.log("Last changed by: " + dsInfo.sourceLastChangedBy);
|
|
|
|
if (dsInfo.sourceLastRefreshedAt) {
|
|
console.log("Last refresh: " +
|
|
dsInfo.sourceLastRefreshedAt.toISOString());
|
|
}
|
|
}
|
|
|
|
// Display in UI
|
|
Text_ModelInfo.setText("Model: " + dsInfo.modelName);
|
|
}
|