Form Handling with JavaScript
APD provides JavaScript functions for handling form data, managing field values, and submitting forms via AJAX.
Form Field Naming Convention
Table:Field Format
All form fields that bind to Business Central data use this format:
TableNo:FieldNo
Examples:
| BC Field | HTML Name |
|---|---|
| Customer.No. | 18:1 |
| Customer.Name | 18:2 |
| Item.Description | 27:3 |
| Sales Line.Quantity | 37:15 |
Field Reference
<!-- Customer Name (Table 18, Field 2) -->
<input type="text" name="18:2" value="<?field(Customer,Name)?>">
<!-- Item Unit Price (Table 27, Field 18) -->
<input type="number" name="27:18" value="<?field(Item,Unit Price)?>">
Getting and Setting Values
APD_GetValue(form, index)
Gets the value of a form field at the specified index.
Parameters:
| Parameter | Type | Description |
|---|---|---|
| form | HTMLFormElement | The form |
| index | number | Element index |
Returns: Field value as string
Handling by Type:
| Input Type | Return Value |
|---|---|
| checkbox | "true" or "false" |
| select-one | Selected option value |
| text/other | Field value |
Example:
var form = document.getElementById('myForm');
var name = APD_GetValue(form, 0); // First field
var city = APD_GetValue(form, 1); // Second field
APD_SetValue(form, index, value)
Sets the value of a form field.
Parameters:
| Parameter | Type | Description |
|---|---|---|
| form | HTMLFormElement | The form |
| index | number | Element index |
| value | string | Value to set |
Handling by Type:
| Input Type | Behavior |
|---|---|
| checkbox | Sets checked if value is "1" |
| select-one | Selects matching option |
| text/other | Sets value directly |
Example:
var form = document.getElementById('myForm');
APD_SetValue(form, 0, 'Contoso Ltd.'); // Set name
APD_SetValue(form, 1, 'Seattle'); // Set city
APD_GetSingleValue(table, field)
Gets a field value by table and field number from any form on the page.
Parameters:
| Parameter | Type | Description |
|---|---|---|
| table | number | Table number |
| field | number | Field number |
Returns: Field value or null
Example:
// Get customer number from any form
var customerNo = APD_GetSingleValue(18, 1);
// Get item description
var itemDesc = APD_GetSingleValue(27, 3);
Form Submission
AJAX Insert
Create a new record:
<form id="newCustomerForm">
<input type="hidden" name="TABLE" value="18">
<label>Name:</label>
<input type="text" name="18:2" required>
<label>Address:</label>
<input type="text" name="18:5">
<label>City:</label>
<input type="text" name="18:7">
<button type="button" onclick="APD_Insert(this.form, 18)">
Create Customer
</button>
</form>
JavaScript:
function createCustomer() {
var form = document.getElementById('newCustomerForm');
APD_Insert(form, 18);
// Form fields are updated with created record data
}
AJAX Modify
Update an existing record:
<?open(Customer)?>
<?filter(Customer,"No.",Parm('custno'))?>
<?findfirst(Customer)?>
<form id="editCustomerForm">
<input type="hidden" name="TABLE" value="18">
<!-- Primary key as hidden input for APD_Modify (<?key?> would write a URL query fragment, not form fields) -->
<input type="hidden" name="18:1" value="<?field(Customer,"No.")?>">
<label>No.:</label>
<input type="text" value="<?field(Customer,"No.")?>" disabled>
<label>Name:</label>
<input type="text" name="18:2" value="<?field(Customer,Name)?>">
<label>Address:</label>
<input type="text" name="18:5" value="<?field(Customer,Address)?>">
<button type="button" onclick="APD_Modify(this.form, 18)">
Save Changes
</button>
</form>
Form Validation Before Submit
function submitForm() {
var form = document.getElementById('orderForm');
// Client-side validation
var qty = APD_GetSingleValue(37, 15);
if (!qty || parseFloat(qty) <= 0) {
APDAlert('Please enter a valid quantity');
return false;
}
// Submit via AJAX
APD_Modify(form, 37);
return true;
}
Field Focus and Original Values
APD_Focus(entry, table, field)
Captures the original value when a field receives focus. This enables reverting to the original value if validation fails.
Parameters:
| Parameter | Type | Description |
|---|---|---|
| entry | HTMLElement | The input element |
| table | number | Table number |
| field | number | Field number |
Usage:
<input type="text"
name="18:2"
onfocus="APD_Focus(this, 18, 2)"
onchange="APD_validate(this, 18, 2)">
How it works:
- User focuses on field
APD_Focus()stores current value inxRecValue- User modifies value and leaves field
APD_validate()is called- If validation fails, value reverts to
xRecValue
Working with Different Input Types
Text Fields
<input type="text"
name="18:2"
value="<?field(Customer,Name)?>"
onfocus="APD_Focus(this, 18, 2)"
onchange="APD_validate(this, 18, 2)">
Checkboxes
<input type="checkbox"
name="27:19"
value="true"
<?if(field,Item,Blocked,=,true)?> checked<?endif?>
onchange="APD_validate(this, 27, 19)">
Getting checkbox value:
// Returns "true" or "false"
var blocked = APD_GetValue(form, checkboxIndex);
Select/Dropdown
<select name="36:70"
onchange="APD_validate(this, 36, 70)">
<option value="0">Open</option>
<option value="1">Released</option>
<option value="2">Pending Approval</option>
</select>
Setting select value:
// By value
APD_SetValue(form, selectIndex, '1'); // Selects "Released"
// By index (if value is numeric)
APD_SetValue(form, selectIndex, 1);
Date Fields
<input type="date"
name="36:19"
value="<?field(Sales Header,Order Date,<Year4>-<Month,2>-<Day,2>)?>"
onchange="APD_validate(this, 36, 19)">
With Date Picker:
<input type="text"
name="36:19"
value="<?field(Sales Header,Order Date)?>"
onclick="APD_UseDatePicker(this)">
Number Fields
<input type="number"
name="37:15"
value="<?field(Sales Line,Quantity)?>"
step="1"
min="0"
onfocus="APD_Focus(this, 37, 15)"
onchange="APD_validate(this, 37, 15)">
Form Examples
Complete Edit Form
<?open(Customer)?>
<?filter(Customer,"No.",Parm('id'))?>
<?findfirst(Customer)?>
<form id="customerForm" name="customerForm">
<!-- Hidden form fields -->
<input type="hidden" name="TABLE" value="18">
<input type="hidden" name="ACTION" value="CUSTOMER-EDIT">
<!-- Primary key as a hidden input (<?key?> is for URL query strings, not form bodies) -->
<input type="hidden" name="18:1" value="<?field(Customer,"No.")?>">
<!-- Visible fields -->
<div class="form-group">
<label>Customer No.</label>
<input type="text" value="<?field(Customer,"No.")?>" disabled>
</div>
<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)"
required>
</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>
<div class="form-group">
<label>Email</label>
<input type="email"
name="18:102"
value="<?field(Customer,E-Mail)?>"
onfocus="APD_Focus(this, 18, 102)"
onchange="APD_validate(this, 18, 102)">
</div>
<div class="form-group">
<label>Blocked</label>
<input type="checkbox"
name="18:39"
value="true"
<?if(field,Customer,Blocked,=,true)?> checked<?endif?>
onchange="APD_validate(this, 18, 39)">
</div>
<div class="form-actions">
<button type="button" onclick="APD_Modify(this.form, 18)">Save</button>
<a href="<?action(CUSTOMERS)?>" class="btn-cancel">Cancel</a>
</div>
</form>
Order Entry Form with Lines
<!-- Header -->
<form id="orderHeader" name="orderHeader">
<input type="hidden" name="TABLE" value="36">
<!-- Primary key of Sales Header: field 1 (Document Type), field 3 (No.) -->
<input type="hidden" name="36:1" value="<?field("Sales Header","Document Type")?>">
<input type="hidden" name="36:3" value="<?field("Sales Header","No.")?>">
<div class="header-fields">
<label>Order No.</label>
<input type="text" value="<?field("Sales Header","No.")?>" disabled>
<label>Customer</label>
<input type="text"
name="36:2"
value="<?field("Sales Header","Sell-to Customer No.")?>"
onfocus="APD_Focus(this, 36, 2)"
onchange="APD_validate(this, 36, 2)">
<button type="button"
onclick="APD_Lookup(36, 2, 18, 1, 2, this)">...</button>
</div>
</form>
<!-- Lines -->
<?open("Sales Line")?>
<?filter("Sales Line","Document No.",field("Sales Header","No."))?>
<?loop("Sales Line",'-')?>
<form id="line<?loopno("Sales Line")?>" name="37:<?loopno("Sales Line")?>">
<input type="hidden" name="TABLE" value="37">
<!-- Sales Line primary key: Document Type (1), Document No. (3), Line No. (4) -->
<input type="hidden" name="37:1" value="<?field("Sales Line","Document Type")?>">
<input type="hidden" name="37:3" value="<?field("Sales Line","Document No.")?>">
<input type="hidden" name="37:4" value="<?field("Sales Line","Line No.")?>">
<input type="text"
name="37:6"
value="<?field("Sales Line","No.")?>"
onfocus="APD_Focus(this, 37, 6)"
onchange="APD_validate(this, 37, 6)">
<input type="number"
name="37:15"
value="<?field("Sales Line",Quantity)?>"
onfocus="APD_Focus(this, 37, 15)"
onchange="APD_validate(this, 37, 15)">
<span><?field("Sales Line","Unit Price")?></span>
<span><?field("Sales Line","Line Amount")?></span>
<button type="button"
onclick="APD_Modify(this.form, 37)">Save Line</button>
</form>
<?endloop("Sales Line")?>
<?close("Sales Line")?>
Post-Validation Handler
ExtraValidateFunction
Define custom logic to run after validation:
var ExtraValidateFunction = function(form) {
// Recalculate totals
updateOrderTotals();
// Enable/disable fields based on values
var status = APD_GetSingleValue(36, 70);
if (status === '1') { // Released
disableEditing();
}
// Update UI
updateLineHighlighting();
};
Best Practices
Always Include Primary Key
For URL-style posts: append <?key(Table)?> to the action URL of the form. For AJAX submits via APD_Modify / APD_Insert (which read the form body, not the URL): emit one <input type="hidden" name="TableNo:FieldNo" value="..."> per primary-key field. <?key?> only writes a URL query fragment — it does not emit hidden form fields.
Use Proper Event Order
<input onfocus="APD_Focus(this, T, F)"
onchange="APD_validate(this, T, F)">
Handle Readonly Fields
<!-- Don't include name attribute for display-only fields -->
<input type="text" value="<?field(Table,Field)?>" disabled>
Validate Before Navigating
function beforeLeaving() {
if (hasUnsavedChanges()) {
return confirm('You have unsaved changes. Continue?');
}
return true;
}