Fields on pages are not just fields!

In this video, I showcase that the “field” construct on a page is much more than just a method for placing table fields on a page.

https://youtu.be/fXDxAM0ZrvU

This is another installment in my series of video for beginners into AL development.


In this video, Erik explores a commonly misunderstood concept in AL development for Business Central: the field control on pages is not limited to displaying table fields. The second parameter of a field control is actually an expression, which opens up a world of possibilities including function calls, calculations, labels, and boolean expressions. This insight came from a LinkedIn discussion and demonstrates how to build smarter, more dynamic page layouts.

The Basics: Creating a Simple Page

Erik starts by creating a straightforward customer card page in Visual Studio Code to establish a baseline. The page has a source table of Customer and displays two fields: Number and Name.

page 50100 "Test Customer Card"
{
    PageType = Card;
    SourceTable = Customer;
    Caption = 'Test Customer Card';
    UsageCategory = Administration;
    ApplicationArea = All;

    layout
    {
        area(Content)
        {
            field("No."; Rec."No.")
            {
                ApplicationArea = All;
            }
            field(Name; Rec.Name)
            {
                ApplicationArea = All;
            }
        }
    }
}

This produces exactly what you’d expect — a card page with two fields showing the customer number and name. Nothing surprising here. But now let’s look deeper at what a field actually is.

Understanding the Field Control’s Two Parameters

The field control takes two parameters: name and expression. The tooltip in VS Code can be slightly misleading because the first parameter is not the name of a field in the table — it’s the name of the visual control on the page. The second parameter is a true expression, not just a field reference.

Expressions Can Be Calculations

Since the second parameter is an expression, you can do more than just reference a field. For example:

field("No."; Rec."No." + ' ABC')
{
    ApplicationArea = All;
}

Running this shows “10000 ABC” as the number value. Suddenly, this is no longer just a field — it’s a transformed expression. The field control is simply displaying the result of whatever expression you give it.

Using Function Calls as Expressions

Taking this concept further, Erik creates a procedure that returns a value and uses it as the field expression:

field(SmartFieldControl; SmartField())
{
    ApplicationArea = All;
    Caption = 'My Smart Field';
}

// ...

procedure SmartField(): Text
begin
    exit('Hello YouTube World');
end;

A few important things to note here:

  • The control name (SmartFieldControl) cannot be the same as the procedure name — you’ll get a compiler error if they match.
  • Since a function call doesn’t have an inherent caption like a table field does, you need to explicitly set the Caption property.
  • The system automatically derives the control’s display type from the return type of the function.

Passing Parameters for Resilient Code

Erik demonstrates a more robust approach by passing the record as a parameter to the function rather than relying on the implicit Rec variable:

field(Control2; SmarterField(Rec))
{
    ApplicationArea = All;
    Caption = 'Smarter';
}

// ...

procedure SmarterField(Customer: Record Customer): Text
begin
    if Random(100) > 50 then
        exit('Smarter ' + Customer.Name)
    else
        exit('Bluer ' + Customer.Name);
end;

Erik advocates strongly for a functional programming approach: functions should only work on the parameters they’re given and only return values through parameters and return values. This makes them far more resilient and reusable across different scenarios. If a function relies on the global Rec variable, it becomes much harder to guarantee you’re operating on the correct record.

When this page is converted to a list with a repeater, each row correctly evaluates the expression independently, with some rows showing “Smarter” and others showing “Bluer” thanks to the random logic.

Different Return Types

The expression’s return type determines how the field control renders the data. Erik demonstrates this with a date return type:

procedure SmartDate(): Date
begin
    exit(20201224D);
end;

The system recognizes the return type as Date and formats it accordingly. Switching the return type to Decimal causes the system to format the value with the standard two decimal places, regardless of how many digits you put in the source:

procedure SmartDate(): Decimal
begin
    exit(123.456789);
end;

The compiler and rendering engine understand the type of the expression and format the display appropriately. However, there are some types you cannot return and display in a field: lists, records, and variants are among the unsupported types.

Labels as Field Expressions

One of the most interesting tricks Erik shows is using a label as the field expression. This is a technique that Microsoft themselves use — for example, in the Transformation Rules page:

field(LabelField; ThisIsALabel)
{
    ApplicationArea = All;
    ShowCaption = false;
    Editable = false;

    trigger OnDrillDown()
    begin
        // Your action code here
        Message('Hello');
    end;
}

// ...

var
    ThisIsALabel: Label 'This is a label';

When a label-type field has an OnDrillDown trigger, it renders as a clickable link — essentially a menu item or action embedded directly in the page layout. By setting ShowCaption = false, you get a clean, standalone link. Erik used this technique in his AL Compiler project to create a horizontal menu bar with Evaluate, Download, and Run options.

Boolean Expressions

Erik wraps up with one more experiment — using a boolean expression as the field value:

field(BoolField; Random(500) > 50)
{
    ApplicationArea = All;
    Caption = 'Boolean Expression';
}

This works perfectly, rendering as a checkbox that evaluates differently for each record. The field control truly handles any valid expression type.

Practical Applications

This understanding of fields-as-expressions has several practical applications:

  • Displaying derived data: Show calculated values from related records without needing FlowFields. For example, displaying a “hero item” description from a purchase line directly on the purchase header list.
  • Replacing global variable patterns: In older versions, developers would create global variables and populate them in OnAfterGetRecord. With expressions, you can simply call a function directly — cleaner and more maintainable.
  • Creating interactive links: Using labels with drill-down triggers to build navigation elements within page layouts.
  • Conditional display logic: Using boolean or calculated expressions to show dynamic status indicators.

For reference, here’s the source code Erik was working with, showing the basic page structure:

page 50100 "Page Type Demo"
{
    PageType = List;
    SourceTable = Customer;
    layout
    {
        area(Content)
        {
            field("No."; Rec."No.")
            {
                ApplicationArea = all;
            }
            field(Name; Rec.Name)
            {
                ApplicationArea = all;
            }
        }
    }
}

Conclusion

The key takeaway is simple but powerful: the field keyword on a page is a visual control, not a direct reference to a table field. Its second parameter is a full expression that can be a field reference, a calculation, a function call, a label, or even a boolean expression. The system determines the display type from the expression’s return type and renders accordingly. Understanding this distinction opens up significantly more flexible page designs and cleaner code patterns in your AL development. Don’t let the name “field” limit your thinking — it’s really an expression container that can display virtually any computed value.