Simple Real-World Patterns

Forsta HX Platform - API Scripting Guide

Simple Real-World Patterns (Forsta Standard)

Pattern 1: Sum Selected Checkboxes (Most Common)

// Calculate total spend from selected options - STANDARD PATTERN
var totalSpend = 0; // Initialize total

// Get array of all possible option codes (e.g., ["1", "2", "3"])
var codes = f('SpendQ').domainValues();

// Loop through each option code
for (var i = 0; i < codes.length; i++) {
    // Check if this option is selected
    if (f('SpendQ').item(codes[i]).toBoolean()) {
        // Add this option's numeric value to the total
        totalSpend += f('SpendQ').item(codes[i]).toNumber();
    }
}

// Set the calculated total
f('TotalSpend').set(totalSpend);

Pattern 2: Check Specific Option and Show Follow-up

// If user selects option "3", show follow-up - Forsta method
// .item('3') accesses the option with code "3"
// .toBoolean() returns true if that option is selected
if (f('AQ1').item('3').toBoolean()) {
    f('AQ1a').show(); // Show "Please explain" textbox (jQuery)
} else {
    f('AQ1a').hide(); // Hide it (jQuery)
}

Pattern 3: Count Selections and Validate

// Ensure user selects 3-5 options - Forsta method
var codes = f('BQ2').domainValues(); // Get all option codes
var count = 0; // Initialize counter

// Count how many are selected
codes.forEach(function(code) {
    if (f('BQ2').item(code).toBoolean()) {
        count++; // Increment counter if selected
    }
});

// Validate the count is in acceptable range
if (count < 3 || count > 5) {
    alert('Please select between 3 and 5 options');
    return false; // Validation failed
}

// Save the count and return success
f('SelectionCount').set(count);
return true;

Pattern 4: Calculate Weighted Score

// Calculate score with different weights - Forsta method
var codes = f('RatingQ').domainValues(); // Get all option codes
var weightedScore = 0; // Initialize weighted total

// Define weight multipliers for each option code
var weights = {'1': 1, '2': 2, '3': 3, '4': 5, '5': 8};

// Loop through each option
codes.forEach(function(code) {
    // Check if this option is selected
    if (f('RatingQ').item(code).toBoolean()) {
        // Get the numeric value of the option
        var value = f('RatingQ').item(code).toNumber();
        
        // Multiply value by weight (use 1 if weight not found)
        weightedScore += value * (weights[code] || 1);
    }
});

// Save the weighted score
f('WeightedScore').set(weightedScore);

Pattern 5: Compare Two Questions

// Find options selected in both questions - Forsta method
var codes = f('PreQ').domainValues();
var matchCount = 0;

codes.forEach(function(code) {
    var inPre = f('PreQ').item(code).toBoolean();
    var inPost = f('PostQ').item(code).toBoolean();
    
    if (inPre && inPost) {
        matchCount++;
    }
});

f('MatchingSelections').set(matchCount);

Pattern 6: Yes/No with Follow-up (jQuery for show/hide)

// Use Forsta to check value, jQuery to show/hide
f('AQ1').on('change', function() {
    var value = f('AQ1').get(); // Get with Forsta
    
    if (value == '1') {
        f('AQ2').show(); // Show with jQuery
        f('AQ3').show();
    } else {
        f('AQ2').hide(); // Hide with jQuery
        f('AQ3').hide();
    }
});

Pattern 7: Convert and Format Values

// Convert field value to string and format
if (f('userid').toBoolean()) {
    var userID = f('userid').toString().toLowerCase();
    var formattedID = userID + '-2024';
    f('FormattedUserID').set(formattedID);
}

// Create utility function for formatting
function padNumber(num, length) {
    var str = String(num);
    while (str.length < length) {
        str = "0" + str;
    }
    return str;
}

// Usage
var patientNum = 5;
var formatted = padNumber(patientNum, 3); // "005"
f('PatientID').set(formatted);

Pattern 8: Conditional Execution (Production vs Test)

// Only run code in production environment
if (IsInProductionMode()) {
    // Production-only logic
    if (f('userid').toBoolean()) {
        var userID = f('userid').toString();
        f('ProcessedUserID').set(userID.toUpperCase());
    }
} else {
    // Test environment - use dummy data
    f('ProcessedUserID').set('TEST-USER-001');
}

Pattern 9: Loop Through Grid Rows and Set Values

// Set values for each row in a grid/loop question
// Get array of all row codes (e.g., ["1", "2", "3"])
var rows = f('GridQuestion').domainValues();

// Loop through each row
for (var i = 0; i < rows.length; i++) {
    var rowNum = parseInt(rows[i]); // Convert row code to integer
    
    // Generate a value for this specific row
    var calculatedValue = 'Row-' + rowNum + '-Value';
    
    // Set value for this specific row using .item(rowCode).set()
    // .item(rows[i]) targets the specific row
    // .set(calculatedValue) sets the value for that row
    f('GridQuestion').item(rows[i]).set(calculatedValue);
}

// Alternative: Using forEach with conditional logic
rows.forEach(function(row) {
    if (someCondition) { // Your condition here
        var value = calculateValue(row); // Your calculation
        // Set value for this row
        f('GridQuestion').item(row).set(value);
    }
});

Pattern 10: Environment-Specific Processing

// Different logic for test vs production
if (isTestID()) {
    // Test ID - use test data
    var testValue = 'TEST-' + Date.now();
    f('ProcessedData').set(testValue);
}
else if (f('userid').toBoolean()) {
    // Real user - process normally
    var uid = f('userid').get();
    var realValue = processUserData(uid);
    f('ProcessedData').set(realValue);
}