Lookup Windows
APD provides lookup functionality that opens popup windows for selecting values from Business Central tables, similar to the lookup experience in BC pages.
APD_Lookup Function
Syntax
APD_Lookup(tableNo, fieldNo, lookupTableNo, lookupFieldNo, descriptionFieldNo, entry)
Parameters:
| Parameter | Type | Description |
|---|---|---|
| tableNo | number | Target table number |
| fieldNo | number | Target field number |
| lookupTableNo | number | Lookup source table |
| lookupFieldNo | number | Primary lookup field |
| descriptionFieldNo | number | Description field to display |
| entry | HTMLElement | Triggering element |
Basic Usage
<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>
In this example:
- 18: Customer table
- 86: Salesperson Code field
- 13: Salesperson/Purchaser table
- 1: Code field (lookup key)
- 2: Name field (description)
Common Lookups
Salesperson Lookup
<!-- For Customer.Salesperson Code -->
<input type="text" name="18:86" value="<?field(Customer,Salesperson Code)?>">
<button onclick="APD_Lookup(18, 86, 13, 1, 2, this)">...</button>
Customer Lookup
<!-- For Sales Header.Sell-to Customer No. -->
<input type="text" name="36:2" value="<?field(Sales Header,Sell-to Customer No.)?>">
<button onclick="APD_Lookup(36, 2, 18, 1, 2, this)">...</button>
Item Lookup
<!-- For Sales Line.No. (Item) -->
<input type="text" name="37:6" value="<?field(Sales Line,No.)?>">
<button onclick="APD_Lookup(37, 6, 27, 1, 3, this)">...</button>
Country/Region Lookup
<!-- For Customer.Country/Region Code -->
<input type="text" name="18:35" value="<?field(Customer,Country/Region Code)?>">
<button onclick="APD_Lookup(18, 35, 9, 1, 2, this)">...</button>
Payment Terms Lookup
<!-- For Customer.Payment Terms Code -->
<input type="text" name="18:8" value="<?field(Customer,Payment Terms Code)?>">
<button onclick="APD_Lookup(18, 8, 3, 1, 3, this)">...</button>
How Lookups Work
Opening the Lookup
- User clicks lookup button
APD_Lookup()is called- Form data is collected for context
- Popup window opens with lookup page
URL Parameters
The lookup action receives these parameters:
| Parameter | Description |
|---|---|
_WINDOW |
Parent window name |
_PTABLE |
Target table number |
_PFIELD |
Target field number |
_TABLE |
Lookup table number |
_FIELD |
Lookup key field |
_FIELD2 |
Description field |
Plus all current form field values for filtering context.
Selecting a Value
- User selects a value in the popup
APD_LookupReturn(value)is called- Value is placed in the target field
APD_validate()is triggered if onchange handler exists- Popup closes
APD_LookupReturn Function
Syntax
APD_LookupReturn(value)
Called from: The lookup popup window when user makes a selection
How It Works
- Gets target table/field from URL parameters
- Finds the matching input in the parent window
- Sets the value using
APD_SetValue() - Triggers validation if configured
- Closes the popup window
Lookup Page Template
The lookup page receives the source table and fields via TABLE, FIELD, and FIELD2 URL parameters. APD does not allow nested tags, so you cannot write <?open(<?parm(TABLE)?>)?> — instead, build one lookup template per target table (e.g. one for Salesperson, one for Customer), or use a Codeunit / Script action that resolves the parameter and outputs the markup.
Example: a dedicated Salesperson lookup template hard-codes the source table and fields:
<!DOCTYPE html>
<html>
<head>
<title>Select Salesperson</title>
<script src="<?action('apdscript')?>"></script>
<style>
table { width: 100%; border-collapse: collapse; }
tr { cursor: pointer; }
tr:hover { background: #f0f0f0; }
td { padding: 8px; border-bottom: 1px solid #ddd; }
</style>
</head>
<body>
<h2>Select Salesperson</h2>
<?open("Salesperson/Purchaser")?>
<table>
<thead>
<tr>
<th>Code</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<?loop("Salesperson/Purchaser",'-')?>
<tr onclick="APD_LookupReturn('<?field("Salesperson/Purchaser",Code)?>')">
<td><?field("Salesperson/Purchaser",Code)?></td>
<td><?field("Salesperson/Purchaser",Name)?></td>
</tr>
<?endloop("Salesperson/Purchaser")?>
</tbody>
</table>
<button onclick="window.close()">Cancel</button>
</body>
</html>
Custom Lookup Implementation
Creating a Custom Lookup
For more control, create your own lookup logic:
<input type="text" id="customerNo" name="36:2">
<button type="button" onclick="openCustomerLookup()">Select Customer</button>
<script>
function openCustomerLookup() {
var popup = window.open(
'?action=CUSTOMER-LOOKUP',
'CustomerLookup',
'width=600,height=400,scrollbars=yes'
);
}
function selectCustomer(customerNo, customerName) {
document.getElementById('customerNo').value = customerNo;
document.getElementById('customerName').textContent = customerName;
// Trigger validation
var input = document.getElementById('customerNo');
APD_validate(input, 36, 2);
}
</script>
Custom Lookup Page
<!DOCTYPE html>
<html>
<head>
<title>Select Customer</title>
</head>
<body>
<h2>Select Customer</h2>
<input type="text" id="search" placeholder="Search..."
oninput="filterCustomers(this.value)">
<?open(Customer)?>
<div id="customerList">
<?loop(Customer,'-')?>
<div class="customer-row"
onclick="selectCustomer('<?field(Customer,"No.")?>', '<?field(Customer,Name)?>')">
<strong><?field(Customer,"No.")?></strong>
<span><?field(Customer,Name)?></span>
<span><?field(Customer,City)?></span>
</div>
<?endloop(Customer)?>
</div>
<script>
function selectCustomer(no, name) {
window.opener.selectCustomer(no, name);
window.close();
}
function filterCustomers(searchText) {
var rows = document.querySelectorAll('.customer-row');
rows.forEach(function(row) {
var text = row.textContent.toLowerCase();
row.style.display = text.includes(searchText.toLowerCase()) ? '' : 'none';
});
}
</script>
</body>
</html>
Lookup with Filters
Passing Filter Context
Current form values are automatically passed to the lookup:
<!-- Sales Line lookup -->
<input type="text" name="37:1" value="Sales"> <!-- Document Type -->
<input type="text" name="37:3" value="ORD001"> <!-- Document No. -->
<!-- Item lookup uses above context -->
<input type="text" name="37:6" value="">
<button onclick="APD_Lookup(37, 6, 27, 1, 3, this)">...</button>
Using Filters in Lookup
<!-- In lookup template -->
<?open(Item)?>
<?filter(Item,Blocked,false)?>
<?filter(Item,"Sales Blocked",false)?>
<table>
<?loop(Item,'-')?>
<tr onclick="APD_LookupReturn('<?field(Item,"No.")?>')">
<td><?field(Item,"No.")?></td>
<td><?field(Item,Description)?></td>
</tr>
<?endloop(Item)?>
</table>
Styling Lookup Buttons
Icon Button
<div class="lookup-field">
<input type="text" name="18:86" value="<?field(Customer,Salesperson Code)?>">
<button type="button"
class="lookup-btn"
onclick="APD_Lookup(18, 86, 13, 1, 2, this)">
<svg viewBox="0 0 24 24" width="16" height="16">
<path d="M7 10l5 5 5-5z"/>
</svg>
</button>
</div>
.lookup-field {
display: flex;
gap: 0;
}
.lookup-field input {
flex: 1;
border-right: none;
border-radius: 4px 0 0 4px;
}
.lookup-btn {
padding: 8px 12px;
border: 1px solid #ccc;
border-left: none;
border-radius: 0 4px 4px 0;
background: #f5f5f5;
cursor: pointer;
}
.lookup-btn:hover {
background: #e0e0e0;
}
Lookup with Validation
Ensure validation triggers after lookup selection:
<input type="text"
name="37:6"
value="<?field(Sales Line,No.)?>"
onfocus="APD_Focus(this, 37, 6)"
onchange="APD_validate(this, 37, 6)">
<button onclick="APD_Lookup(37, 6, 27, 1, 3, this)">...</button>
The APD_LookupReturn function checks for onchange handlers:
// In APD_LookupReturn
if (attr[a].name == "onchange") {
if (attr[a].value.toString().slice(0,8) == 'Navision') {
APD_validate(pwin.document.forms[w].elements[d], ptable, pfield);
}
}
Best Practices
Provide Both Text Entry and Lookup
Allow users to type directly or use lookup:
<input type="text" name="18:86"
onfocus="APD_Focus(this, 18, 86)"
onchange="APD_validate(this, 18, 86)">
<button onclick="APD_Lookup(...)">...</button>
Show Current Selection
Display the description alongside the code:
<div class="lookup-group">
<input type="text" name="36:2"
value="<?field(Sales Header,Sell-to Customer No.)?>">
<span id="customerName"><?field(Sales Header,Sell-to Customer Name)?></span>
<button onclick="APD_Lookup(...)">...</button>
</div>
Handle Large Lists
For tables with many records, implement search/filter in the lookup:
<!-- Lookup template with search -->
<input type="text" id="searchBox"
placeholder="Type to search..."
onkeyup="if(event.key==='Enter') doSearch()">
<button onclick="doSearch()">Search</button>
Disable When Appropriate
Disable lookup for non-editable records. The <?if?> tag takes Table,Field,Operator,Value directly — do not nest a <?field?> tag inside it:
<?if("Sales Header",Status,'<>',Open)?>
<input type="text" value="<?field("Sales Header","Sell-to Customer No.")?>" disabled>
<?else?>
<input type="text" name="36:2" value="<?field("Sales Header","Sell-to Customer No.")?>">
<button onclick="APD_Lookup(36, 2, 18, 1, 2, this)">...</button>
<?endif?>