AL pages are the primary way users interact with data in Business Central. This post covers three common page types you will build in almost every extension: a Card page for single-record editing, a List page for browsing records, and a ListPart for embedding related data inside another page as a FactBox.
The examples use a custom table called Contact Note (fictional, but realistic) and extend the standard Customer Card to show related notes in the FactBox pane.
Prerequisites
- Visual Studio Code installed with the AL Language extension
- A Business Central sandbox environment connected in
launch.json - An existing AL extension project (
app.jsonconfigured, symbols downloaded) - Basic familiarity with AL table objects
Steps
1. Define the source table
Before building the pages, create the table that will back the ListPart. This table stores notes linked to a customer.
table 50100 "Contact Note"
{
Caption = 'Contact Note';
DataClassification = CustomerContent;
fields
{
field(1; "Entry No."; Integer)
{
Caption = 'Entry No.';
AutoIncrement = true;
}
field(2; "Customer No."; Code[20])
{
Caption = 'Customer No.';
TableRelation = Customer."No.";
}
field(3; "Note Date"; Date)
{
Caption = 'Note Date';
}
field(4; Description; Text[250])
{
Caption = 'Description';
}
field(5; "Created By"; Code[50])
{
Caption = 'Created By';
}
}
keys
{
key(PK; "Entry No.") { Clustered = true; }
key(CustomerKey; "Customer No.", "Note Date") { }
}
}
2. Create the Card page
A Card page displays one record at a time. Set PageType = Card and point SourceTable at your table.
page 50100 "Contact Note Card"
{
PageType = Card;
ApplicationArea = All;
UsageCategory = Documents;
SourceTable = "Contact Note";
Caption = 'Contact Note';
layout
{
area(content)
{
group(General)
{
Caption = 'General';
field("Entry No."; Rec."Entry No.")
{
ApplicationArea = All;
Editable = false;
}
field("Customer No."; Rec."Customer No.")
{
ApplicationArea = All;
}
field("Note Date"; Rec."Note Date")
{
ApplicationArea = All;
}
field(Description; Rec.Description)
{
ApplicationArea = All;
MultiLine = true;
}
field("Created By"; Rec."Created By")
{
ApplicationArea = All;
Editable = false;
}
}
}
}
actions
{
area(processing)
{
action(NewNote)
{
ApplicationArea = All;
Caption = 'New Note';
Image = New;
trigger OnAction()
begin
Rec.Init();
Rec."Note Date" := Today();
Rec."Created By" := UserId();
Rec.Insert(true);
end;
}
}
}
}
3. Create the List page
A List page shows multiple records in a grid. Set PageType = List and link CardPageId to the Card page you just created so users can drill down.
page 50101 "Contact Note List"
{
PageType = List;
ApplicationArea = All;
UsageCategory = Lists;
SourceTable = "Contact Note";
CardPageId = "Contact Note Card";
Caption = 'Contact Notes';
Editable = false;
layout
{
area(content)
{
repeater(Lines)
{
field("Entry No."; Rec."Entry No.")
{
ApplicationArea = All;
}
field("Customer No."; Rec."Customer No.")
{
ApplicationArea = All;
}
field("Note Date"; Rec."Note Date")
{
ApplicationArea = All;
}
field(Description; Rec.Description)
{
ApplicationArea = All;
}
field("Created By"; Rec."Created By")
{
ApplicationArea = All;
}
}
}
}
actions
{
area(processing)
{
action(NewNote)
{
ApplicationArea = All;
Caption = 'New Note';
Image = New;
RunObject = page "Contact Note Card";
RunPageMode = Create;
}
}
}
}
4. Create the ListPart
A ListPart is a stripped-down list intended to be embedded inside another page. Set PageType = ListPart. It does not have its own navigation or UsageCategory, it is always hosted by a parent page.
page 50102 "Contact Note Factbox"
{
PageType = ListPart;
ApplicationArea = All;
SourceTable = "Contact Note";
Caption = 'Contact Notes';
Editable = false;
layout
{
area(content)
{
repeater(Lines)
{
field("Note Date"; Rec."Note Date")
{
ApplicationArea = All;
}
field(Description; Rec.Description)
{
ApplicationArea = All;
}
field("Created By"; Rec."Created By")
{
ApplicationArea = All;
}
}
}
}
actions
{
area(processing)
{
action(OpenList)
{
ApplicationArea = All;
Caption = 'Open Full List';
Image = List;
RunObject = page "Contact Note List";
}
}
}
}
5. Add the ListPart as a FactBox on the Customer Card
Use a pageextension to add your ListPart to the existing Customer Card. The SubPageLink filters the ListPart to only show notes for the current customer.
pageextension 50100 "Customer Card Ext" extends "Customer Card"
{
layout
{
addfirst(factboxes)
{
part(ContactNoteFactbox; "Contact Note Factbox")
{
ApplicationArea = All;
Caption = 'Contact Notes';
SubPageLink = "Customer No." = field("No.");
}
}
}
}
6. Compile and publish
Press Ctrl + Shift + B to compile the extension. If there are no errors, press F5 to publish to your sandbox.
Open the Customer Card for any customer and check the FactBox pane on the right. The Contact Notes part should appear and show only notes linked to that customer.
If the FactBox does not appear, make sure you have not hidden it using Personalise settings in your sandbox user profile.
Common Mistakes
- Forgetting
SubPageLinkon thepart, without it, the ListPart shows all records, not just those for the current record. - Setting
UsageCategoryon a ListPart, ListPart pages should not have aUsageCategorybecause they are not standalone pages. - Using
PageType = Listinstead ofListPartwhen you intend to embed, a regular List page cannot be placed in the FactBox pane.
Next Steps
Once your pages are working, you may want to trigger logic when records are saved, see How to Use Events and Subscribers in Business Central AL Development for how to hook into Business Central’s event model from your extension.