JavaScript API Overview
APD provides a JavaScript library that enables AJAX communication with Business Central, allowing you to build dynamic, interactive web portals without full page refreshes. The library is served by APD itself as an action — reference it from your templates via <?action('apdscript')?>.
Getting Started
Including the Library
The library is served as an APD action. Reference it from your templates with the <?action?> tag — APD expands it to the full URL that returns the JavaScript:
<script src="<?action('apdscript')?>"></script>
Do not ship a static
apdscript.jsfile with your templates. APD generates the script per request so thatRootLocationand other settings are populated from APD Setup automatically.
Configuration
RootLocation (the base URL the library posts AJAX requests back to) is populated automatically by APD when the script is generated — there is no file to edit. The value comes from the Serverpath field on APD Setup (the same value <?apd?> returns).
Core Concepts
Form Field Naming
APD uses a specific naming convention for form fields:
Format: TableNo:FieldNo
| Element | Example |
|---|---|
| Customer Name | 18:2 |
| Item Description | 27:3 |
| Sales Line Quantity | 37:15 |
AJAX Operations
APD supports these AJAX operations:
| Operation | Function | Purpose |
|---|---|---|
| Validate | APD_validate() |
Validate and trigger field logic |
| Find | APD_Find() |
Search for records |
| Insert | APD_Insert() |
Create new records |
| Modify | APD_Modify() |
Update existing records |
| Lookup | APD_Lookup() |
Open lookup windows |
Response Format
AJAX responses follow this pattern:
| Prefix | Meaning |
|---|---|
DATA1 |
Success with data |
ERROR |
Error occurred |
EMPTY |
No records found |
Function Reference
Data Validation
APD_validate(entry, table, field)
Validates a field and triggers Business Central validation logic.
<input type="text"
name="18:2"
onchange="APD_validate(this, 18, 2)"
value="<?field(Customer,Name)?>">
Record Operations
APD_Insert(form, table)
Inserts a new record.
<form onsubmit="APD_Insert(this, 27); return false;">
<input type="text" name="27:3" placeholder="Description">
<button type="submit">Create Item</button>
</form>
APD_Modify(form, table)
Modifies an existing record.
<form onsubmit="APD_Modify(this, 18); return false;">
<input type="hidden" name="TABLE" value="18">
<!-- For AJAX modify, emit the primary key field(s) as hidden inputs.
<?key?> is only for URL query strings, not form bodies. -->
<input type="hidden" name="18:1" value="<?field(Customer,"No.")?>">
<input type="text" name="18:2" value="<?field(Customer,Name)?>">
<button type="submit">Save</button>
</form>
Record Navigation
APD_Find(table, step, viewString, inputForm, outputForm)
Finds records with navigation.
// Find first record
APD_Find(18, '-', 'undefined', 'myform', 'myform');
// Find next record
APD_Find(18, '>', 'undefined', 'myform', 'myform');
// Find previous record
APD_Find(18, '<', 'undefined', 'myform', 'myform');
Value Helpers
APD_GetValue(form, index)
Gets a value from a form field.
var name = APD_GetValue(document.forms[0], 2);
APD_SetValue(form, index, value)
Sets a value in a form field.
APD_SetValue(document.forms[0], 2, 'New Value');
APD_GetSingleValue(table, field)
Gets a single field value by table and field number.
var customerNo = APD_GetSingleValue(18, 1);
Session Storage
APD_Set(key, value)
Stores a value in the session.
APD_Set('lastSearch', 'widgets');
APD_Get(key)
Retrieves a stored value.
var lastSearch = APD_Get('lastSearch');
APD_Has(key)
Checks if a key exists.
if (APD_Has('lastSearch')) {
// Key exists
}
Lookup Windows
APD_Lookup(tableNo, fieldNo, lookupTable, lookupField, descField, entry)
Opens a lookup window.
<button onclick="APD_Lookup(18, 86, 13, 1, 2, this)">
Select Salesperson
</button>
Alerts
APDAlert(message)
Shows an alert message (customizable).
APDAlert('Record saved successfully!');
You can override the default alert:
APDAlert = function(message) {
// Custom alert implementation
showMyModal(message);
};
Examples
AJAX Form Example
<?open(Customer)?>
<?filter(Customer,"No.",Parm('custno'))?>
<?findfirst(Customer)?>
<form id="customerForm">
<input type="hidden" name="TABLE" value="18">
<!-- Primary key as a hidden input (APD_Modify reads form elements) -->
<input type="hidden" name="18:1" value="<?field(Customer,"No.")?>">
<div class="form-group">
<label>Name</label>
<input type="text"
name="18:2"
value="<?field(Customer,Name)?>"
onfocus="APD_Focus(this, 18, 2)"
onchange="APD_validate(this, 18, 2)">
</div>
<div class="form-group">
<label>Address</label>
<input type="text"
name="18:5"
value="<?field(Customer,Address)?>"
onfocus="APD_Focus(this, 18, 5)"
onchange="APD_validate(this, 18, 5)">
</div>
<div class="form-group">
<label>City</label>
<input type="text"
name="18:7"
value="<?field(Customer,City)?>"
onfocus="APD_Focus(this, 18, 7)"
onchange="APD_validate(this, 18, 7)">
</div>
<button type="button" onclick="APD_Modify(this.form, 18)">Save</button>
</form>
Lookup with Validation
<div class="input-group">
<input type="text"
name="18:86"
value="<?field(Customer,Salesperson Code)?>"
onfocus="APD_Focus(this, 18, 86)"
onchange="APD_validate(this, 18, 86)">
<button type="button"
onclick="APD_Lookup(18, 86, 13, 1, 2, this)">
...
</button>
</div>
Navigation Buttons
<div class="record-nav">
<button onclick="APD_Find(18, '-', 'undefined', 'custform', 'custform')">
First
</button>
<button onclick="APD_Find(18, '<', 'undefined', 'custform', 'custform')">
Previous
</button>
<button onclick="APD_Find(18, '>', 'undefined', 'custform', 'custform')">
Next
</button>
<button onclick="APD_Find(18, '+', 'undefined', 'custform', 'custform')">
Last
</button>
</div>
Custom Validation Handler
// Define extra validation function
var ExtraValidateFunction = function(form) {
// Calculate totals after validation
var qty = parseFloat(APD_GetValue(form, 2));
var price = parseFloat(APD_GetValue(form, 3));
var total = qty * price;
document.getElementById('total').textContent = total.toFixed(2);
};
Error Handling
Built-in Error Display
APD uses APDAlert() for error messages. Override for custom handling:
APDAlert = function(message) {
var errorDiv = document.getElementById('error-message');
errorDiv.textContent = message;
errorDiv.style.display = 'block';
setTimeout(function() {
errorDiv.style.display = 'none';
}, 5000);
};
Error Codes
Check errorCode after validation:
function afterValidate(form) {
if (errorCode === 0) {
console.log('Validation successful');
} else {
console.log('Validation failed');
}
}
Best Practices
Field Focus
Always use APD_Focus() to track original values:
<input onfocus="APD_Focus(this, 18, 2)"
onchange="APD_validate(this, 18, 2)">
Form Naming
Name forms for easier reference:
<form name="customerForm" id="customerForm">
Loading States
Show loading indicators during AJAX:
function showLoading() {
document.body.classList.add('loading');
}
function hideLoading() {
document.body.classList.remove('loading');
}
Validation Chaining
Use ExtraValidateFunction for post-validation logic:
var ExtraValidateFunction = function(form) {
updateTotals();
checkRequiredFields();
enableSubmitButton();
};
Security Considerations
- AJAX endpoints are secured by APD's security system
- Table permissions apply to AJAX operations
- Security filters are enforced server-side
- Validate all input on the server, not just client