Basic Newsletter AL Extension
I’m not going to rewrite the guide for setting up your VS Code environment for Extension development, but we’ll go through the basic steps of creating your extension assuming you have a functional NAV/BC environment to work with.
In our VS Code window, Alt-A then Alt-L will trigger the AL Project Creation process, asking us where to put the extension. For sanity, I’m going to be using a folder structure like so:
Our AL Extension does not need the heaping pile of source library files that our Angular App will, so we’ll keep them separate.
After you give the Extension Creation process a folder, it will ask which server you are working with, Cloud or On-Prem. I’m using on prem, so it asks me for some authentication. Odds are pretty high your authentication will fail. You’ll see some errors in the Output Panel:
Mostly I’m getting Reason: Not Found errors and you probably will too. It defaults to looking for an instance on localhost called BC130, not the On-Prem default, DynamicsNAV130. Thankfully it drops you right to your new launch.json file. Change your Server and Server Instance as appropriate.
You may also need to update the Authentication from UserPassword to Windows, depending on your installation.
Since I’m going to be adding the jumping off point in my extension to the Segments List of the system, I’m also going to take the opportunity to change the startupObjectID from 22 (Customer List) to Page 5093 “Segment List”. Tip: If you aren’t 100% sure of the Page ID, you can usually find it in the URL:
When you hit Save on this file, you should get a pop-up that notices you don’t have your Dynamics Symbols from the server. Hit Download symbols to correct that.
If you don’t get this pop-up, press Ctrl+Shift+P and select AL: Download symbols.
You’ll probably want to update the app.json file next with info relevant to you. More details on that here. Make sure you have a strategy around your idRange with your organization/customers.
For our demo, we’ll use object range 73000 to 73050. When I save this change, my HelloWorld.al file will go red in the list, indicating a problem, but that’s ok, we’re going to delete it. Go ahead and do that now.
Now, strangely, Microsoft’s “AL Go!” we ran to create the basic folder/files? That doesn’t setup the folders to match their guidelines. It’s just a nearly empty project.
We’ll create the 3 Microsoft recommended folders, src, res, and test. We’ll create two objects, one a page, one an extension to add the button to Segments. We’re looking at this:
I personally find Pag5093-Ext73001.AddHTMLSend.al just awkward to type or retain, but I presume this is a useful naming schema at some point.
Opening up Pag73000.SendHTML.al, type tp to get the snippets to offer a template to you:
You get a lot of content here (way more than I’ll show):
Skipping ahead, we’ll have this as the content of Pag73000.SendHTML.al:
Now hitting F5 will run the solution with your extension installed and active. I’ve noticed that here and there I sometimes have to force refresh my browser to see the changes, but that’s no big deal (Ctrl+F5 and it’s fine).
Great, the page exists and is working. Now let’s do more interesting things with it.
Building the Control Add-In Wrapper
Open up our startup.js file and add this extensive pile of code:
Open AngularWrapper.js now and define a basic function:
Next, open up HTMLEditor.al and use the tcontrolpanel snippet to get a massive pile of settings. We’ll replace that with
This won’t do much, but we can test that the Control Addin works by listening for the ControlReady event in AL.
Back in the Pag73000.SendHTML.al file, we can now add our usercontrol to the layout, like so:
Connecting the systems
OK, so, if you skipped ahead, dropped the JS and CSS files from your Angular into the Control Addin folders, added all three JS files to your AL file, ran the Extension, and looked at the Console errors, you’d see this mess:
Or more likely, you’d see this baffling error stack:
Two different errors I’ve mentioned our need to avoid.
In the former, our Angular is looking for <app-root> in the HTML, which won’t exist. The latter is because the three Angular scripts MUST load in the right order, in serial not parallel.
If we just add the following JS to init(), we can solve the first issue, right?
(Note: the autogenerated HTML has a DIV with the ID controlAddIn so we can do this sort of DOM modification.)
Baffling, yes? Here’s the “help” on the Scripts Property (emphasis mine):
Although this property is optional, the control add-in must either specify the StartupScript property or specify one or more scripts. Scripts can be either external resources referenced using a URL or can be embedded within the extension. Embedded script files must be added to the extension project folder in Visual Studio Code and referenced using a relative path. For security and usability reasons, it is recommended to reference any external scripts by using the HTTPS protocol. Scripts are loaded immediately when the control add-in is initialized.
So… what precisely is the difference? One takes a list, one takes a single file – but both are executed immediately. Useless for our needs.
This means we must handle loading the scripts dynamically, in order. But, NAV/BC don’t let us load script resources. We have precisely two methods for getting anything:
- GetEnvironment – this lets us find out information like the platform, company
- GetImageResource – this gives us the URL of an image that was in our Addin Manifest as it can be found by a browser
That’s all we have. A lot of trial error and help from Netronic’s fabulous Blog Series on their similar battles and I found how to hijack the GetImageResources call to load JS files later.
First, we need to put all our files in the right place. This will get annoying to do manually and we’ll have to do this every time we build our Angular solution, so a quick batch file should help.
In VS Code, though, let’s go ahead and just Add Folder to Workspace to bring the Angular solution into the same workspace. Now in our Angular HTMLEditor folder, make a file called angularbuild.bat and add all this, updated to match your paths:
Run that and you should end up with something like this in your AL ControlAddin folder:
Back in HTMLEditor.al, we need to add a weird section:
Now, we still have ANOTHER weird barrier with GetImageResource – it’s not path aware! I think this may be a bug, so we’ll see if this remains necessary.
If we were to call Microsoft.Dynamics.NAV.GetImageResource(‘runtime.js’), it will return:
So, the server extracts the Addin with the folder hierarchy intact, but GetImageResource ignores that entirely. (Aside: I have yet to see the temp folder where addins extract to ever empty out. This could be a large problem. Perhaps a defect?)
Thankfully, AL has string replace() to rescue us. Here’s the crazy nested code you’ll need to load the app in the right order:
Now a reload of everything should finally result in:
It’s not perfect and it doesn’t DO anything yet, but if you’ve made it this far in your copy, you technically have made an Angular application work inside NAV/Business Central.