If you’re just joining us, please make sure to check the Table of Contents in the first post here: Post 1 – Angular inside Dynamics 365 Business Central.
Send Our Data Between Embedded Angular and Dynamics 365 Business Central
One of the challenges to doing development work with Add-ins when you’re used to working C/AL or AL is that NAV doesn’t make you think about Synchronous vs Asynchronous exactly. Triggers/events are known, mapped, and happen in order. Code within them is executed in order, and that’s that.
There is a little bit of guidance on the Developer Documentation site, which is short enough to quote here in its entirety:
Writing control add-ins that work on all display targets, you have to consider some limitations regarding asynchronous communication. Compared to the Microsoft Dynamics NAV Windows client control add-in extensibility framework, the Dynamics NAV framework has some limitations regarding the interface of the control add-in. The limitations come from the nature of the asynchronous communication between the clients and the Microsoft Dynamics NAV Server. All calls between the C/AL code running on the Microsoft Dynamics NAV Server and the script function running in the Web browser are asynchronous. This means that methods in the control add-in interface must be of type void and property methods should not be used.
To transfer a result from a C/AL trigger to the calling script function, just add a method to the control add-in interface that the C/AL trigger can invoke to send the result to the script.
To transfer a result from a script function to C/AL trigger, just add an event to the control add-in interface that the script function can use to invoke a C/AL trigger that receives the result.
For our extension, we have a tiny to-do list:
- When the control loads or is refreshed, we need to take the data and send it to Angular to display
- When Angular changes the data, we should fire an event to AL to receive and save it.
Send the Data
Our flow needs to be this, which we’ll develop from end back to start:
We have to build all those connections ourselves, all the way back. For Angular to be reachable from JS, we need to create some hooks into the Global scope outside of Angular.
Dive back into your the-editor.component.ts file. We’ll first add a new function to update the value of our HTML string:
We’ll also replace the constructor with this odd bit of code:
Easy mental shortcut: Zone is akin to Threading or AppPools.
You’ll also noticed we added the ControlReady call here – we don’t want to send the ControlReady event from the Wrapper anymore – we want to know Angular is doing initializing. Also, since TypeScript is trying to help us out, and it doesn’t know about the NAV Extensibility, we’ll tell it to ignore the line.
Back in AL, in our /res/ControlAddin/Script/AngularWrapper.js file, we now need to add a function call to that new set of hooks on the window object.
This gets the Zone and triggers a run command to run our function.
Also, in our startup.js file, remove the InvokeExtensibilityMethod call.
Still in AL, in our /res/ControlAddin/HTMLEditor.al file, we need to add the Function to the AL object so we can call it elsewhere. That is just a one line add towards the end of our object:
In this example, as you may recall, we’re not dealing with saved HTML, just in memory as a temporary action page. For a proper full solution, we’d do a lot more.
Given that, we only have to add a little code to our Pag73000.SendHTML.al file, in the trigger ControlReady:
We can also comment out or delete our earlier test message.
We theoretically send that rawHTML text variable to the HTML EditorControl, only, it’s not databound, so we need to mock-send data. For that, I’ll add a new Action to the page. Our new Page looks like so:
Do a build on the Angular objects to get the latest in place, then we can run a test on our new Extension updates.
Now we have a new Action:
And when we run that, we should see:
Update the Data
In Angular, we have an easy way to hook into the Change monitoring system, and we’ll fire our event from that.
Note: This Change Event fires with pretty much each keystroke, so use this very carefully in a Validation chain. See here if you want to see how you can make it driven by a button press instead.
Fire the Event
In the-editor.component.html, we update our single line (with added line breaks for visual clarity to say:
In the-editor.component.ts, we add a new function:
Combining those two, whenever Angular detects a change to the content of the NgxEditor control, it will fire an event to NAV called HTMLEditor, passing the new value of that string variable, htmlContent.
Go ahead and run the build batch file again, as we need the .js files updated.
Make the Event Available
Making the Control Addin handle the event is as easy as that previous event definition for ControlReady. In HTMLEditor.al, add:
Handle the Event
In our Extension Pag73000.SendHTML.al, we need to add a bit of code: page / layout / area / group / UserControl(“HTMLEditor”):
Since we’re not actually doing anything with the data since this isn’t a real solution, so let’s also add a way to see the results via a new Action
This will show us the raw HTML.
Now, let’s try it out!
I’ll add some formatted text to the end of our TestHTML string, then click ShowHTML:
Technically we’re done here! We have handled:
- Creating a basic Angular application
- Creating an AL Extension
- Connecting AL to Angular
- Passing data into Angular from NAV
- Receiving a new event to data back from Angular
This is a really basic implantation, but when you remember that AL has built-in handling for JSON, so we can pass pretty huge chunks of data around.
We’ll address a really complex topic in the bonus blog post to follow: Resize awareness.
If you do make anything with all this, please, let me know! I’ll happily sign NDAs just to see what fun things people make.