Documentation

Support

Extensible Controls in Dynamics 365 for Finance and Operations (Tutorial)

Date

Friday, September 8, 2017

Posted by

Julio Luzardo

In Dynamics 365 for Finance and Operations (D365) Extensible Controls take on the role of the Managed Host Control of AX 2012. In AX 2012 the Managed Host Control could either be a Windows Forms Control or a WPF control. In its simplest form in D365 an Extensible Control is a piece of HTML with JavaScript functionality.

Although at first it might seem that Extensible Controls cannot carry out many of the tasks of Managed Host Controls and that Extensible Controls are limited, let me assure you that it isn't the case. From a UX perspective, HTML5 coupled with JavaScript can do anything that a WF or WPF control can do. It can even outperform them. Given the fact that HTML5 and JavaScript are present in so many devices, and in so many different platforms, it is beyond doubt the user interface language of choice by today's standards.

Now having said that, developing Extensible Controls is much more complicated than writing WF or WPF controls. In this tutorial, we will try to demystify that complexity. To understand this tutorial you must have a basic knowledge of HTML, CSS, JavaScript, JQuery and X++.

We are going to build an interactive tile navigation menu that you can include in any form. It will have some tiles that when clicked will take the user to another form. The tiles will have two sizes and the user will be able to change the background color of the control. This tutorial does not include data binding and will not involve the build class.

At the end of this tutorial you will know:

How to trigger an action in JavaScript from X++. A user presses a button on the form and the control displays a message box.

How to use properties and the observable pattern. A user selects a color from a drop-down and changes the background color for the control.

How to use a method to load data into the control using JSON. Tile information such as position, size, caption and display menu item are loaded from X++ during run time.

How to use a method to trigger an action in X++ that originates in JavaScript. A user clicks on a tile in the control and gets redirected to the corresponding D365 for Finance and Operations page via the associated Display Menu Item for the tile.

Control Physical Structure

An Extensible Control is made up of five files:

An X++ build class. Which houses the control's design time properties menu. We won't go into this class in this tutorial.

An X++ runtime class. Which contains properties and methods which we can call from JavaScript or from X++.

An HTML resource file.

A JavaScript resource file.

A CSS resource file. Optional.

Create the Basic Extensible Control Project Structure

1. Create a new model called NavMenu in a new package and make it reference Application Foundation and Application Platform.

2. Create a new operations project. Call this new project "NavigationMenu".

3. Change the project's model to NavMenu. Right click on the project in solution explorer and click properties.

The JavaScript inside the control can receive instructions from X++. The JavaScript can also send instructions to X++ and in this operation, receive JSON data from X++. It's a two-way street.

To demonstrate sending commands from X++ to JavaScript we're going to add code to the X++ runtime class (NAMNavigationMenuCtrl) and to JavaScript. The objective is that when a user clicks a button in the NATForm1 form a message box is displayed by the control.

1. Add this class level variable to the NAMNavigationMenuCtrl class:

private FormProperty m_dtSendCommandToJavaScript;

2. In the NAMNavigationMenuCtrl class constructor initialize the variable:

11. Press F5 to start debugging (or Debug -> Start Debugging). Every time you click on the "Click Me" button you will get a "SendCommandToJavaScript Invoked" message box.

In JavaScript the this.SendCommandToJavaScript variable is created because the "SendCommandToJavaScript" getter/setter in the X++ runtime class is exposed (public). The naming of the variable in JavaScript depends on the FormPropertyAttribute's second parameter. If you were to change the getter/setter attribute to this:

Only when the this.SendCommandToJavaScript value changes. So now you're probably wondering what's the purpose of the m_bSendCommandToJavaScript variable. Its purpose is to prevent the code from firing when the control is being initialized. The value of this.SendCommandToJavaScript variable will inevitably change from nothing to something when being initialized. We only need to display the message box when the user clicks the button and not when the form is loading. You can try removing the variable and observing the effect.

And that finally takes us to the getter/setter and why we're using utcdatetime.

Every time this getter/setter is called the system date/time will inevitably change and the observable in JavaScript will be triggered.

This is not the intended use of observable properties. But they are useful when we want to trigger an event from X++. We will now go into the original intended usage of observable properties.

In NATForm1 we want to have a FormMenuButtonControl to allow the user to change the background color of the navigation menu control.

1. If you want to avoid having unnecessary code erase the FormProperty, the FormProperty initialization and the getter/setter from the X++ runtime class from the previous demonstration. Also erase the observable function from JavaScript and the "Click Me" button in the form.

13. Press F5 or Debug -> Start Debugging and using the "Background color" menu change the background color of the control.

Loading Data into The Control Using JSON

Properties can be used to pass scalar variables of data between X++ and the control. But what if you need an array or a matrix (table)? In the Navigation Menu Control the requirement is that for each tile the X & Y position, the size, the caption, the display menu item be stored in a table and then passed at runtime. For the purposes of this tutorial we won't actually use a table, but we will pretend that the data came from a table.

To pass information from JavaScript to X++ and back Extensible Controls use JSON. JSON is short for JavaScript Object Notation and it can be thought of as a lightweight version of XML. Dynamics 365 for Finance and Operations has its own JSON serializer inside the class FormJsonSerializer.

So that we can easily serialize to JSON we have to create two new classes and both are extensions of FormDataContract. The first one is NAMTiles which has a method of type List and can hold several objects of type NAMTile which is our second class. We create several NAMTile objects, group them together in NAMTiles and with a single line of X++ code we serialize and get our JSON string which we then send back over the wire. When it reaches the JavaScript we deserialize with a single line of JS code and start building the tiles inside the control.

1. Create a class called NAMTile. This class represents one tile. Include the following code inside this class:

3. Create a method in the X++ runtime class. This method will be called by JavaScript and will return the list of tiles back to JavaScript in JSON format. It does not have any parameters but methods can have parameters of type str, int, etc.

5. Add the following code to JavaScript after the $dyn.observe function. The m_AddTilesToControl function is responsible for adding the Tiles to the control as soon as GetTiles returns. This function gets a JSON string from GetTiles in X++. The $dyn.callFunction line is responsible for calling the X++ GetTiles method in the X++ runtime class, passing the parameters (empty in this case) and setting the callback function (m_AddTilesToControl).

7. Press F5 to debug or Debug -> Start Debugging. This is now the new appearance of the control:

Sending Commands From JavaScript to X++

The final part of this tutorial will show you how to invoke actions in X++ code triggered by user actions in the control. In the Navigation Menu control whenever a user clicks on a tile the form that the tile represents will open.

1. Create a global object outside the main JavaScript function. The reason we're creating it outside is because it will contain the click event handler that will be called by the tiles and has to be outside of the scope of the main function.

GlobalObj = {};
(function () {
…
})();

2. Create this JavaScript function immediately after the $dyn.observe function. This function will handle the onclick event from any tile and call the OpenForm method on the X++ runtime class. It can be triggered by the tile itself or by the child span object, so it first checks if the object has an id, if it does not then it is a span object and we need to reference the parent object instead.

5. Press F5 or Debug -> Start Debugging, click on any tile and the associated form will be opened.

Additional Notes

1. Although it isn't demonstrated in this tutorial, it is possible to initiate a call from X++ like in the SendCommandToJavaScript example, and then have the JavaScript execute a $dyn.callFunction in the $dyn.observe function and have the control receive JSON data from the X++ runtime class.

2. Sometimes when calling the $dyn.callFunction method the call will not execute:

Because the framework is in an interaction. This generally happens when $dyn.callFunction calls are deeply nested in code. The framework will however alert you via the browser's console. When debugging it is always important to monitor the browser's console (you can do this by pressing the F12 key in most browsers). In those cases you must "yield" the JavaScript code. When you "yield" a call you do not call the function directly, you call it via window.setTimeout and you convert a synchronous call into an asynchronous one and the call will then execute when the framework is ready. This is a simple example: