In this video, I show how to add a data editor to Business Central. I’m implenenting the Ace editor. This is the editor I use in several projects, including BCScript and WSFN from E Foqus.
In this video, Erik demonstrates how to embed a fully-featured code editor directly inside Business Central using the Ace editor — a popular JavaScript-based editor — as a control add-in. This allows you to view and edit ASCII data such as JSON, XML, Pascal/AL source code, and more, complete with syntax highlighting, bracket matching, and search functionality.
What is the Ace Editor?
Ace is a mature, JavaScript-based code editor originally maintained as the primary editor for the Cloud9 IDE. It’s the successor to the Mozilla Skywriter (Bespin) project. Ace provides a rich editing experience inside a web page, including:
- Syntax highlighting for over 200 languages
- Multiple themes (Dracula, Monokai, etc.)
- Search and replace functionality
- Bracket matching
- Extensible via modes, themes, and extensions
Embedding Ace into a page is remarkably simple. A minimal setup involves a <div> element, a reference to the ace.js script, and a few lines of JavaScript to initialize the editor, set a theme, and set the language mode.
You can grab pre-built versions of Ace from the ace-builds repository on GitHub. The repository contains both minified and non-minified versions, along with mode files for each supported language and various theme files.
Setting Up the Control Add-In
The first step is to create a control add-in definition in AL. You need to specify which JavaScript files from Ace should be included, along with your own startup and script files. Only include the language modes you actually need — the full Ace library with all 200+ languages would make your app unnecessarily large and slow down deployment.
Here’s the control add-in definition that Erik builds during the video:
controladdin SourceEditor
{
Scripts =
'SourceEditor/ace.js',
'SourceEditor/ext-searchbox.js',
'SourceEditor/mode-pascal.js',
'SourceEditor/mode-json.js',
'SourceEditor/theme-dracula.js',
'SourceEditor/script.js';
StartupScript = 'SourceEditor/startup.js';
RequestedHeight = 450;
event ControlReady();
event SaveRequested(source: Text);
procedure Init();
procedure LoadDocument(docText: Text);
procedure SetLanguage(language: Text);
procedure RequestSave();
}
A few important things to note:
- The
Scriptsproperty lists all JavaScript files that need to be transferred and made available to the control. - The
StartupScriptis the script that runs when the control is loaded — it signals to Business Central that the control is ready. RequestedHeightgives the control some vertical space on the page.- Events (
ControlReady,SaveRequested) allow the JavaScript side to communicate back to AL. - Procedures (
Init,LoadDocument,SetLanguage,RequestSave) allow AL to call into the JavaScript side.
The Startup Script
The startup script is minimal — its job is simply to tell Business Central that the control add-in is ready:
Microsoft.Dynamics.NAV.InvokeExtensibilityMethod('ControlReady', []);
This is critical because without it, you could end up in a situation where AL code tries to interact with the control before it has finished loading.
The Main Script
The main script file contains the JavaScript functions that correspond to the procedures defined in the control add-in:
var editor;
function Init() {
editor = ace.edit('controlAddIn');
editor.setTheme('ace/theme/dracula');
editor.session.setMode('ace/mode/pascal');
}
function LoadDocument(docText) {
editor.setValue(docText);
}
function SetLanguage(language) {
editor.session.setMode('ace/mode/' + language);
}
function RequestSave() {
Microsoft.Dynamics.NAV.InvokeExtensibilityMethod('SaveRequested', [editor.getValue()]);
}
Key points about this script:
- The
editorvariable is kept in global scope so all functions can access it. - The
Initfunction initializes Ace on thecontrolAddIndiv — this is the div that Business Central automatically creates inside the iframe for your control add-in. LoadDocumentuses Ace’ssetValue()method to populate the editor with content.SetLanguagedynamically switches the syntax highlighting mode.RequestSaveusesInvokeExtensibilityMethodto fire theSaveRequestedevent back to AL, passing the current editor content viaeditor.getValue().
The Test Page
With the control add-in defined, creating a page to use it is straightforward:
page 55500 "Source Editor Test Page"
{
PageType = Card;
layout
{
area(Content)
{
usercontrol(Editor; SourceEditor)
{
ApplicationArea = all;
trigger ControlReady()
begin
CurrPage.Editor.Init();
CurrPage.Editor.SetLanguage('json');
//CurrPage.Editor.LoadDocument('procedure test(a : Integer);');
CurrPage.Editor.LoadDocument('{ "test" : 123 }');
end;
trigger SaveRequested(source: Text)
begin
message('The Source is %1', source);
end;
}
}
}
actions
{
area(Processing)
{
action(Save)
{
Promoted = true;
PromotedCategory = Process;
PromotedIsBig = true;
PromotedOnly = true;
ApplicationArea = all;
Caption = 'Save';
trigger OnAction()
begin
CurrPage.Editor.RequestSave();
end;
}
}
}
}
Note that while you define a controladdin object in AL, when you place it on a page it becomes a usercontrol. This naming inconsistency is a common source of confusion.
Understanding the Asynchronous Communication Pattern
One of the most important concepts when working with control add-ins is that communication is asynchronous. Procedures that call into a control add-in cannot have return values. This means you can’t simply write a Save() function that returns the editor’s text.
Instead, you need a two-step pattern:
- AL calls a procedure (
RequestSave) on the control add-in — this is the request. - JavaScript fires an event (
SaveRequested) back to AL with the data — this is the response.
Erik mentions that his usage of InvokeExtensibilityMethod is deliberately simple and that for more complex scenarios, this function requires more finesse. He recommends checking out Jaeco’s videos for deeper coverage of that specific topic.
API Design Choices
When wrapping a JavaScript library as a control add-in, you have two design approaches:
- Mirror the underlying API — Expose functions like
SetValue()andGetValue()that directly correspond to the Ace editor API. This is great because the existing documentation applies directly. - Adapt to your use case — Create functions like
LoadDocument()andRequestSave()that describe what you’re doing in your business context rather than what the underlying control does.
Erik tends to prefer the latter approach, reasoning that since you’ll never implement all hundreds of functions that a library like Ace provides, it’s better to clearly indicate the intended usage rather than give people false hope of full API coverage.
Deployment Size Considerations
Be mindful of what you include. In this example, the JavaScript files total about 4.1 MB. If you were to include all 200+ language modes, the app would be significantly larger and deployment times would suffer. Only include the modes, themes, and extensions you actually need.
A Sneak Peek: BC Script Mode
Erik briefly teases a project he’s working on that involves a custom Ace mode called “BC Script.” Similar to how Ace’s PHP mode combines HTML, CSS, JavaScript, and PHP highlighting in a single file, this custom mode combines HTML, JavaScript, and Pascal/AL highlighting — essentially supporting a web page with embedded AL code. He hints that this might be the subject of a future video.
Summary
Embedding the Ace editor as a Business Central control add-in is a powerful technique for viewing and editing structured text data with proper syntax highlighting. The key takeaways are:
- Use the Ace editor from the ace-builds repository as your JavaScript foundation.
- Create a control add-in with a startup script that signals readiness, and a main script that bridges Ace’s API with AL procedures and events.
- Remember that communication is asynchronous — use the request/event pattern to get data back from the control.
- Only include the language modes and themes you need to keep deployment sizes manageable.
- The editor is very reliable and can be used for displaying and editing JSON, XML, source code, and other ASCII data directly within Business Central.