Advanced Portal Designer Documentation
Index

AJAX Functions Reference

APD provides a set of AJAX functions for communicating with Business Central without page refreshes.

Core AJAX Functions

ajax_request(url, data, callback, context)

Low-level AJAX request function.

Parameters:

Parameter Type Description
url string Request URL
data string POST data
callback function Callback for async (null for sync)
context object Context passed to callback

Returns: ajax_response object (for sync requests)

Example:

// Synchronous request
var response = ajax_request(
    RootLocation + '?action=MYACTION',
    'param1=value1&param2=value2',
    null,
    null
);

// Asynchronous request
ajax_request(
    RootLocation + '?action=MYACTION',
    'param1=value1',
    function(response) {
        if (response.error) {
            console.error(response.error);
        } else {
            console.log(response.value);
        }
    },
    { myContext: 'data' }
);

ajax_response Object

The response object returned by AJAX requests.

Properties:

Property Type Description
request XMLHttpRequest The raw request object
error ajax_error Error object if failed
value string Response text if successful
context object Context from request

ajax_error Object

Error information for failed requests.

Properties:

Property Type Description
name string Error name
description string Error description
number number Error code

ajax_stop()

Aborts all pending AJAX requests.

Usage:

// Cancel all pending requests
ajax_stop();

APD AJAX Functions

APD_GetAction(action, parms)

Calls an APD action and returns the response.

Parameters:

Parameter Type Description
action string Action code
parms string URL parameters

Returns: Response text

Example:

var html = APD_GetAction('PRODUCT-DETAILS', 'itemno=ITEM001');
document.getElementById('product-panel').innerHTML = html;

APD_Refresh(table, inputForm)

Refreshes form fields with current record data.

Parameters:

Parameter Type Description
table number Table number
inputForm HTMLFormElement Form to refresh

Returns: true if successful, false if no data

Example:

function refreshCustomer() {
    var form = document.getElementById('customerForm');
    if (!APD_Refresh(18, form)) {
        APDAlert('Customer not found');
    }
}

APD_Find(table, step, viewString, inputForm, outputForm)

Finds records in a table with navigation.

Parameters:

Parameter Type Description
table number Table number
step string Navigation direction
viewString string Filter string
inputForm string Form name for input data
outputForm string Form name for output data

Step Values:

Value Description
- First record
+ Last record
< Previous record
> Next record
= Exact match

Returns: true if found, false if not

Examples:

// Find first customer
APD_Find(18, '-', 'undefined', 'custForm', 'custForm');

// Find next customer
APD_Find(18, '>', 'undefined', 'custForm', 'custForm');

// Find with filter
APD_Find(18, '-', 'WHERE(Name=FILTER(@*Smith*))', 'custForm', 'custForm');

APD_Insert(form, table)

Inserts a new record from form data.

Parameters:

Parameter Type Description
form HTMLFormElement Form containing data
table number Table number

Example:

<form id="newItemForm">
    <input type="text" name="27:3" placeholder="Description">
    <input type="number" name="27:18" placeholder="Unit Price">
    <button onclick="APD_Insert(this.form, 27)">Create</button>
</form>

Result Handling:

// Form fields are updated with the inserted record data
// Including any auto-generated values like No.

APD_Modify(form, table)

Modifies an existing record.

Parameters:

Parameter Type Description
form HTMLFormElement Form containing data
table number Table number

Example:

<form id="editForm">
    <input type="hidden" name="TABLE" value="18">
    <!-- Primary key as a hidden input (APD_Modify reads form elements;
         <?key?> would only write a URL query fragment) -->
    <input type="hidden" name="18:1" value="<?field(Customer,"No.")?>">

    <!-- Editable fields -->
    <input type="text" name="18:2" value="<?field(Customer,Name)?>">

    <button onclick="APD_Modify(this.form, 18)">Save</button>
</form>

APD_FillLines(tableNo, parentTableNo, viewString)

Fills multiple forms with line records.

Parameters:

Parameter Type Description
tableNo number Line table number
parentTableNo number Parent table number
viewString string Filter with parent reference

Example:

// Fill sales lines for current order
APD_FillLines(37, 36, 'WHERE(Document No.=FILTER(?1?))');

Session Functions

APD_Set(key, value)

Stores a value in the server session.

Parameters:

Parameter Type Description
key string Storage key
value string Value to store

Returns: true if successful

Example:

// Store user preference
APD_Set('theme', 'dark');
APD_Set('lastCustomer', '10000');

APD_Get(key)

Retrieves a value from the server session.

Parameters:

Parameter Type Description
key string Storage key

Returns: Stored value or null

Example:

var theme = APD_Get('theme');
if (theme === 'dark') {
    document.body.classList.add('dark-mode');
}

APD_Has(key)

Checks if a key exists in session.

Parameters:

Parameter Type Description
key string Storage key

Returns: true if exists

Example:

if (APD_Has('cart')) {
    loadCart();
}

Response Handling

Response Prefixes

APD_validate, APD_Find, APD_Refresh, APD_Insert, and APD_Modify return responses that start with a 5-character prefix:

Prefix Meaning Returned by
DATA1 Success — followed by URL-encoded name=value pairs for the form fields All of the above
ERROR Error — followed by the error message text All of the above
EMPTY No record matched the search APD_Find, APD_Refresh

APD_GetAction does not use these prefixes — it returns the action's raw response body. The other functions are wrappers around APD_validate's envelope-parsing logic and you don't normally inspect the prefix yourself.

Parsing Data Response

if (response.value.slice(0, 5) === 'DATA1') {
    var data = response.value.slice(5);
    var pairs = data.split('&');
    var fields = {};
    
    for (var i = 0; i < pairs.length; i++) {
        var pair = pairs[i].split('=');
        fields[pair[0]] = decodeURIComponent(pair[1]);
    }
    
    // Use fields object
    console.log(fields['18:2']); // Customer Name
}

Error Handling

Default Error Handler

APDAlert = function(message) {
    alert(message);
};

Custom Error Handler

APDAlert = function(message) {
    // Show in custom modal
    var modal = document.getElementById('errorModal');
    modal.querySelector('.message').textContent = message;
    modal.style.display = 'block';
};

Error from URL

APD can pass errors via URL:

// Automatic error display on page load
if (window.location.search.search("_ERRORMESSAGE") !== -1) {
    setTimeout(APD_ShowError, 1000);
}

Utility Functions

enc(s)

Encodes special characters for URL.

var encoded = enc('Hello=World&More');
// Returns: Hello%3DWorld%26More

APD_GetValueFromQueryString(parm)

Gets a parameter from the URL.

var customerId = APD_GetValueFromQueryString('custno');

APD_CreateViewString(parentTableNo, viewString)

Creates a view string with parent field values.

var view = APD_CreateViewString(36, 'WHERE(Document No.=FILTER(?1?))');
// Replaces ?1? with value of field 1 from table 36

Best Practices

Async vs Sync

  • Use async for user-initiated actions
  • Avoid sync requests (blocks UI)

Error Recovery

function safeAjax(action, data, callback) {
    try {
        ajax_request(
            RootLocation + '?' + action,
            data,
            function(response) {
                if (response.error) {
                    APDAlert('Request failed: ' + response.error.name);
                } else {
                    callback(response);
                }
            },
            null
        );
    } catch (e) {
        APDAlert('Network error. Please try again.');
    }
}

Loading Indicators

function withLoading(action) {
    showSpinner();
    action().finally(function() {
        hideSpinner();
    });
}