How-ToAL Development

How to Create Custom Pages and List Parts in AL

Learn how to build a Card page, a List page, and a ListPart in AL, then embed the ListPart as a FactBox using a page extension.

8 min read

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

  1. Visual Studio Code installed with the AL Language extension
  2. A Business Central sandbox environment connected in launch.json
  3. An existing AL extension project (app.json configured, symbols downloaded)
  4. 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 SubPageLink on the part, without it, the ListPart shows all records, not just those for the current record.
  • Setting UsageCategory on a ListPart, ListPart pages should not have a UsageCategory because they are not standalone pages.
  • Using PageType = List instead of ListPart when 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.