Navigation
Aptean Mesh supports multi-page navigation within a mobile app bundle. This tutorial shows how to navigate from a list page to a detail page, pass context data between pages, and handle back navigation.
Prerequisites
Complete the Using a Repeater tutorial first.
Overview
Navigation in Aptean Mesh works in two ways:
- JSON-driven navigation -- Define a
navigateaction in the page contract that pushes a new page onto the navigation stack. - AL-driven navigation -- Use
MobileCommandBuilder220FDWto navigate programmatically from server-side logic, passing data along with the navigation command.
Both approaches push a destination page onto a stack. The mobile app automatically provides a back button to return to the previous page.
Step 1: Navigate from a List Page (JSON)
In your page contract, define a navigate action with a destination matching the target page's ID. Use data to pass values from the current page context:
List Page -- ItemList.json
{
"header": {
"title": "Items",
"refresh": true
},
"body": [
{
"type": "repeater",
"dataKey": "items",
"key": "No",
"template": "CARD",
"onTapAction": "onItemTap",
"emptyMessage": "No items found",
"fields": [
{ "key": "No", "label": "Item No." },
{ "key": "Description", "label": "Description" }
]
}
],
"contract": {
"initialAction": "onLoadItems",
"actions": [
{
"id": "onLoadItems",
"type": "api",
"method": "GetItems",
"params": []
},
{
"id": "onItemTap",
"type": "navigate",
"destination": "ITEM-DETAIL",
"data": { "ItemNo": "{No}" }
}
]
}
}
The {No} syntax interpolates the No field from the tapped repeater row. When the user taps an item with No = "1000", the navigation passes ItemNo = "1000" to the detail page.
Detail Page -- ItemDetail.json
{
"header": {
"title": "Item Detail",
"titleKey": "item.Description",
"refresh": true
},
"body": [
{
"type": "card",
"props": {
"title": "Item Details",
"fields": [
{ "label": "No.", "valueKey": "item.No" },
{ "label": "Description", "valueKey": "item.Description" },
{ "label": "Category", "valueKey": "item.Category" },
{ "label": "Unit Price", "valueKey": "item.UnitPrice" }
]
}
}
],
"contract": {
"initialAction": "onLoadItem",
"actions": [
{
"id": "onLoadItem",
"type": "api",
"method": "GetItem",
"params": ["ItemNo"]
}
]
}
}
The params: ["ItemNo"] sends the navigation parameter to your AL service as an input value.
Step 2: Read Context on the Target Page
On the detail page, the navigation data is available through State.GetInput(). The parameter name (ItemNo) matches the key you used in the data object:
AL Service
codeunit 50100 MyItemService implements IBCRPC220FDW
{
procedure RPC(var State: Codeunit MobileRPCState220FDW) ResponseJson: JsonObject
begin
case State.Method() of
'GetItems':
exit(GetItems(State));
'GetItem':
exit(GetItem(State));
end;
ResponseJson := State.Serialize();
end;
local procedure GetItems(var State: Codeunit MobileRPCState220FDW) Result: JsonObject
var
Item: Record Item;
Items: JsonArray;
ItemObj: JsonObject;
begin
if Item.FindSet() then
repeat
Clear(ItemObj);
ItemObj.Add('No', Item."No.");
ItemObj.Add('Description', Item.Description);
Items.Add(ItemObj);
until Item.Next() = 0;
Result.Add('items', Items);
end;
local procedure GetItem(var State: Codeunit MobileRPCState220FDW) Result: JsonObject
var
Item: Record Item;
ItemNo: Code[20];
Document: JsonObject;
begin
// Read the parameter passed via navigation data
Evaluate(ItemNo, State.GetInput('ItemNo', true));
Item.Get(ItemNo);
Document.Add('No', Item."No.");
Document.Add('Description', Item.Description);
Document.Add('Category', Item."Item Category Code");
Document.Add('UnitPrice', Format(Item."Unit Price"));
Result.Add('item', Document);
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::MobileRPCRegistry220FDW,
OnResolveService, '', false, false)]
local procedure HandleMyService(
CodeunitId: Integer;
var Service: Interface IBCRPC220FDW;
var IsServiceResolved: Boolean)
begin
if IsServiceResolved then
exit;
if CodeunitId = Codeunit::MyItemService then begin
Service := this;
IsServiceResolved := true;
end;
end;
}
The end-to-end flow when a user taps item 1000:
onItemTapaction fires withdata { "ItemNo": "1000" }- App navigates to the
ITEM-DETAILpage ITEM-DETAILrunsinitialAction"onLoadItem"withparams ["ItemNo"]- RPC call to
GetItem--State.GetInput('ItemNo')returns"1000" - Service returns item data; page renders the card
Step 3: Navigate Programmatically from AL
When navigation depends on server-side logic (such as access checks or data lookups), use the MobileCommandBuilder220FDW from within your RPC handler. This is the pattern used throughout the base app:
local procedure SelectDocument(var State: Codeunit MobileRPCState220FDW) Result: JsonObject
var
NavigateData: JsonObject;
DocumentNo: Code[20];
begin
Evaluate(DocumentNo, State.GetInput('documentNo', true));
// Build the navigation data payload
NavigateData.Add('documentNo', DocumentNo);
NavigateData.Add('documentType', 'Receipt');
// Issue a navigate command -- the app pushes the target page
State.Command().Navigate('DETAIL', NavigateData);
Result := State.Serialize();
end;
The State.Command() accessor returns the embedded MobileCommandBuilder220FDW. Calling Navigate() buffers a navigation command that is included in the serialized response. The mobile app processes the command and pushes the destination page.
You can combine navigation with an alert that shows before the page transition:
State.Command().Navigate('DETAIL', NavigateData, 'Document Loaded', 'Opening receipt R-001');
Step 4: Custom Routing via MobileNavigator220FDW
The MobileNavigator220FDW codeunit provides higher-level navigation helpers and a customization point through the OnResolveTarget event. Subscribe to this event to override the default page code or title for a given navigation target:
[EventSubscriber(ObjectType::Codeunit, Codeunit::MobileNavigator220FDW,
OnResolveTarget, '', false, false)]
local procedure CustomizeNavTarget(
Target: Enum MobilePage220FDW;
DocumentNo: Code[20];
var PageCode: Code[20];
var Title: Text;
var TypeText: Text;
var Handled: Boolean)
begin
// Route transfer orders to a custom page
if Target = MobilePage220FDW::Detail then
if IsTransferOrder(DocumentNo) then begin
PageCode := 'TRANSFER-DETAIL';
Title := StrSubstNo('Transfer %1', DocumentNo);
TypeText := 'Transfer';
Handled := true;
end;
end;
The navigator also provides convenience methods for common navigation patterns:
var
Navigator: Codeunit MobileNavigator220FDW;
begin
// Navigate to a detail page with standard title resolution
exit(Navigator.NavigateToDetail(State, MobilePage220FDW::Detail, DocumentNo));
// Navigate to a workflow page with document and line context
exit(Navigator.NavigateToWorkflow(State, MobilePage220FDW::Receipt, DocumentNo, LineNo));
// Navigate back to the home page
exit(Navigator.NavigateHome(State));
// Navigate back to home with a success alert
exit(Navigator.NavBackToHomeWithAlert(State, 'Posted', 'Receipt posted successfully.'));
end;
See MobileNavigator220FDW API reference for all navigation methods and the MobilePage220FDW enum values.
Step 5: Back Navigation
Automatic Back Navigation
The mobile app automatically adds a back button in the header of every page except the root. Users can tap it or use the Android back gesture to return to the previous page.
Programmatic Back Navigation
From AL, issue a NavBack command to pop the current page from the navigation stack:
local procedure DeleteAndGoBack(var State: Codeunit MobileRPCState220FDW) Result: JsonObject
begin
// ... perform delete logic ...
// Navigate back to the previous page
State.Command().NavBack();
Result := State.Serialize();
end;
Show an alert before navigating back:
State.Command().NavBack('Deleted', 'The item has been removed.');
Navigate back to a specific page deeper in the stack (skipping intermediate pages):
// Pop back to the HOME page, skipping any intermediate screens
State.Command().NavBackTo('HOME');
// Pop back with an alert
State.Command().NavBackTo('HOME', 'Receipt Posted', 'Receipt R-001 posted successfully.');
The base app uses NavBackTo after posting a document -- it skips the detail page and returns directly to the home screen with a confirmation message.
Summary
| Approach | When to use |
|---|---|
JSON navigate action | Simple page transitions where no server logic is needed |
State.Command().Navigate() | Navigation that depends on server-side checks or data |
MobileNavigator220FDW helpers | Standard patterns like detail pages and workflows |
OnResolveTarget event | Custom routing based on document type or other criteria |
NavBack() / NavBackTo() | Returning to a previous page after an operation |
What's Next
- Workflows -- Build guided step-by-step flows for warehouse operations