Use JSON or simplify communication with ControlAddIns

The JsonObject and JsonArray data types can be used to simplify the process of transferring data between AL and ControlAddIns. Check out the video:

https://youtu.be/1DxKyvn86Zg

In this video, Erik demonstrates how to use JSON objects to simplify communication between AL (Business Central) and JavaScript in Control Add-ins. Instead of defining multiple parameters on events and procedures — which can get messy and error-prone — you can pass a single JSON object back and forth, keeping your interface clean and flexible.

The Problem: Too Many Parameters

When working with Control Add-ins in Business Central, communication between AL and JavaScript happens through events (JavaScript → AL) and procedures (AL → JavaScript). The traditional approach involves defining multiple parameters on these interfaces:

// The traditional approach with multiple parameters
event IAmReady(name: Text; number: Integer; xDate: Date);

On the JavaScript side, you’d call this event using the extensibility API:

Microsoft.Dynamics.NAV.InvokeExtensibilityMethod('IAmReady', ['Erik', 123, someDate]);

This approach has several drawbacks. As you add more data to exchange, you end up with long parameter lists. Type mismatches (like passing a date from JavaScript to AL) can cause cryptic errors that the debugger struggles to handle. Erik demonstrates this by passing a zero for a date parameter, resulting in an error message like “method with matching argument types not found” — and the debugger can’t even properly break on it.

The Solution: Pass JSON Objects Instead

The much cleaner approach is to define your events and procedures with a single JSON parameter:

event IAmReady(parameters: JsonObject);

On the JavaScript side, you build a JavaScript object (which naturally maps to JSON) and pass it in a single call:

var parameter = {
    name: 'Erik',
    info: {
        area: 'some value'
    }
};

Microsoft.Dynamics.NAV.InvokeExtensibilityMethod('IAmReady', [parameter]);

Back in AL, you receive a JsonObject that you can work with using the standard JSON types. Calling Format() on a JsonObject serializes it to a string, so you can easily inspect what was received:

trigger IAmReady(parameters: JsonObject)
begin
    Message(Format(parameters));
end;

This outputs something like:

{"name":"Erik","info":{"area":"some value"}}

You get all the data in a single parameter — including nested objects — without having to define a complex interface.

Passing Data from AL to JavaScript

The same technique works in the other direction. Define a procedure on the Control Add-in that accepts a JsonObject:

procedure HereisSomeData(data: JsonObject);

In the JavaScript file, implement the function:

function HereisSomeData(data) {
    console.log(data);
    alert(data.name);
}

Erik shows a particularly powerful example: passing an entire Business Central record to JavaScript in a single call. By leveraging a helper codeunit with a RecToJson function (built in a previous video), you can serialize a complete customer record and send it over:

trigger IAmReady(parameters: JsonObject)
var
    JsonTools: Codeunit "Json Tools";
    Customer: Record Customer;
begin
    CurrPage.X.HereisSomeData(JsonTools.RecToJson(Customer));
end;

Debugging Tip: Use console.log

Erik shares a useful debugging trick for the JavaScript side. Instead of trying to use AL’s debugger (which doesn’t understand JavaScript), use console.log in your JavaScript code and open the browser’s developer tools with Ctrl+Shift+I. The logged JSON object will appear in the console, and you can expand it to inspect all the fields and nested objects that were passed from AL.

Working with JSON in AL

The provided source code shows a comprehensive example of building and reading JSON structures in AL. This demonstrates the types you’ll use when processing JSON data received from a Control Add-in:

pageextension 50144 "JSON" extends "Sales Order"
{
    actions
    {
        addfirst(processing)
        {
            action(ShowJson)
            {
                Caption = 'Show JSON';
                Promoted = true;
                PromotedCategory = Process;
                PromotedIsBig = true;
                PromotedOnly = true;
                ApplicationArea = all;
                Image = Document;
                trigger OnAction()
                var
                    obj: JsonObject;
                    O2: JsonObject;
                    item: JsonObject;
                    Token: JsonToken;
                    T2: JsonToken;
                    T3: JsonToken;
                    ja: JsonArray;
                    v: JsonValue;
                    txt: Text;
                begin
                    obj.Add('field', 'Youtube Video');

                    v.SetValue(123.456);
                    obj.Add('Price', v);

                    obj.Add('version', '0.2.0');
                    item.add('No', 20201110D);
                    ja.add(item);
                    clear(item);
                    item.add('No', 20201112D);
                    ja.add(item);
                    ja.Add(100);
                    ja.Add(200);
                    obj.Add('Items', ja);

                    if obj.Contains('Items') then begin
                        obj.get('Items', Token);
                        foreach T2 in Token.AsArray() do begin
                            if T2.IsObject() then begin
                                O2 := T2.AsObject();
                                O2.Get('No', T3);
                                if T3.IsValue() then begin
                                    V := T3.AsValue();
                                    Message('Value as Date %1', CalcDate('+5D', V.AsDate()));
                                end;
                            end;
                        end;
                    end;
                end;
            }
        }
    }
}

This example illustrates several key concepts:

  • JsonObject — represents a JSON object with key/value pairs
  • JsonArray — represents a JSON array that can hold mixed types (objects, numbers, strings)
  • JsonToken — a general-purpose type that can represent any JSON element
  • JsonValue — represents a primitive JSON value (string, number, date, boolean)
  • You can nest objects and arrays to build complex structures
  • When reading, you use type-checking methods like IsObject() and IsValue() to safely navigate the structure

Summary

The key takeaway is simple: instead of defining Control Add-in events and procedures with multiple parameters of specific types, use a single JsonObject parameter. This approach gives you:

  • Cleaner interfaces — one parameter instead of many
  • Flexibility — add or remove fields without changing the interface signature
  • Natural mapping — JavaScript objects map directly to JSON, which maps directly to AL’s JsonObject
  • Complex data support — pass nested objects, arrays, and even entire records in a single call
  • Fewer errors — avoid type mismatch issues that are hard to debug across the AL/JavaScript boundary

The trigger for this video was Erik seeing code with functions called multiple times with long parameter lists — and that’s simply not necessary when you can pass JSON objects back and forth between AL and JavaScript.