Advanced Portal Designer Documentation
Index

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:

  1. User focuses on field
  2. APD_Focus() stores current value in xRecValue
  3. User modifies value and leaves field
  4. APD_validate() is called
  5. 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;
}