Skip to main content

Notifications

A Guide to Simplify Dynamics 365 JavaScript Development using xrm-ex

Introduction

JavaScript development in Dynamics 365 can be challenging due to the lack of IntelliSense and type definitions for critical objects like Xrm and formContext. As JavaScript isn’t a typed language, developers often rely on runtime debugging to determine the methods and properties available on these objects—a process that can slow down development and introduce errors.

The open-source framework xrm-ex addresses these challenges by using JSDoc annotations to enable IntelliSense, allowing you to view available methods and properties directly in your IDE. This guide will walk you through how xrm-ex can streamline Dynamics 365 JavaScript development with examples and setup instructions to help you get started quickly and confidently.

Why xrm-ex?

xrm-ex is designed to minimize boilerplate code and improve code accuracy in Dynamics 365 JavaScript customizations. By defining attributes with field types directly in code, it allows developers to leverage IntelliSense effectively, making for a smoother and more reliable development experience. Let's look at some key features and examples that showcase its capabilities.

1. IntelliSense with Typed Attributes

In traditional Dynamics 365 JavaScript, field definitions lack type specificity, leading to reduced IntelliSense accuracy. With xrm-ex, you define attributes by their type using classes like TextField, LookupField, and OptionsetField, which in turn enables precise IntelliSense suggestions. Here’s how it works:

class Fields {
    Firstname = new XrmEx.Class.TextField("firstname");
    Customer = new XrmEx.Class.LookupField("parentcustomerid");
    PreferredContactMethod = new XrmEx.Class.OptionsetField(
        "preferredcontactmethodcode",
        {
            Any: 1,
            Email: 2,
            Phone: 3,
            Fax: 4,
            Mail: 5,
        }
    );
}

With this approach, IntelliSense not only recognizes each attribute type but also displays the relevant methods for each type. For example, a TextField will offer string-related methods, while a LookupField will provide methods specific to lookups.

2. Enhanced Attribute Controls and Custom Functions

xrm-ex adds custom functions to various field classes, simplifying otherwise complex tasks. For instance, LookupField includes a retrieve function, allowing you to retrieve the record set in the lookup directly:

let customerRecord = await fields.Customer.retrieve();

You can also use addCustomView in LookupField to add a filter to a lookup via FetchXML:

fields.Customer.addCustomView(fetchXml);

In OptionsetField, xrm-ex uses generic types, allowing IntelliSense to suggest only available options when setting values. As shown below, the IntelliSense will suggest "Any", "Email", "Phone", and other options as defined in the example model earlier:


fields.PreferredContactMethod.Value = "Phone";


fields.PreferredContactMethod.Value = fields.PreferredContactMethod.Option.Phone;

3. Simplified Execute Request

In Dynamics 365, executing actions like "QualifyLead" often requires creating complex request objects and specifying detailed metadata for each parameter. Without xrm-ex, this process involves defining extensive configurations, building custom request classes, and manually setting parameter types—making the code lengthy and error-prone.

Example without xrm-ex executeAction.

xrm-ex simplifies this by handling the request setup internally. It requires only the essential parameters, making your code much cleaner and easier to read. Here’s an example of how xrm-ex streamlines action execution:

let response = await XrmEx.executeAction(
    "QualifyLead",
    {
        CreateAccount: true,
        CreateContact: true,
        CreateOpportunity: true,
        SuppressDuplicateDetection: true,
    },
    XrmEx.Form.entityReference
);

With xrm-ex, there’s no need to define custom request classes or handle parameter metadata manually. Instead, you pass in the parameters you need, and xrm-ex manages the rest—allowing you to focus on building features, not boilerplate code.

4. Easy Event Handlers with Array Support

Adding and managing event handlers in Dynamics 365 can get repetitive, especially when dealing with multiple fields or functions. xrm-ex allows you to pass arrays of fields and functions, making it easy to manage complex event handling scenarios.

For example, the following code will add both OnNameChange and OnDataChange functions to the OnChange event of the Firstname and Lastname fields. If you set the last boolean to true, it will immediately trigger fireOnChange for all fields after adding the event functions:


XrmEx.Form.addOnChange(
    [fields.Firstname, fields.Lastname],
    [OnNameChange, OnDataChange],
    true
);

This one-liner not only adds the event handlers to both fields but also triggers the event immediately if needed.

Additional Advantages of xrm-ex

xrm-ex offers many more features. Its type-safe structures and IntelliSense-driven approach make it an excellent tool for both novice and experienced Dynamics 365 developers looking to increase productivity and reduce errors.

Getting Started with xrm-ex

Prerequisites

  1. Install Node.js:
    Before using xrm-ex, make sure you have Node.js installed on your machine. Node.js includes npm (Node Package Manager), which is needed to install xrm-ex. You can download Node.js from nodejs.org.
  2. Recommended IDE:
    For an optimized development experience, I recommend using Visual Studio Code. It’s free, lightweight, and offers excellent IntelliSense support, which is essential when working with xrm-ex.

Steps

Note: You can also find a video guide in the GitHub README.
  • Install xrm-ex via npm: Open a terminal in your project directory and run:
    npm install xrm-ex
  • Use the Template to Set Up Your Namespace:
    After installation, xrm-ex includes a template in node_modules/xrm-ex/README.md to help you structure fields, tabs, and grids in a type-safe way. Before using it, rename the namespace (e.g., YourNamespace.Contact) to fit your project. Using your IDE’s "Rename" function can make this quick and consistent across your code.
  • Add the Template to Your Project: Place the template code in a dedicated script file, such as contactForm.js, to keep your project organized. A full template example is provided at the end of this guide for easy reference.
  • Customize Your Setup:
    • Define your fields, tabs, and grids using xrm-ex classes.
    • Leverage IntelliSense in Visual Studio Code to get tailored code suggestions and easily access methods specific to each field type.

Testing xrm-ex in Dynamics 365

Once you’ve set up your project using xrm-ex, you’ll need to publish the XrmEx.js file to Dynamics 365 and configure it as a form library to make the framework available on your forms. Here’s how to do it step-by-step:
  1. Locate XrmEx.js: The XrmEx.js file is included with xrm-ex and can be found in your project directory under node_modules/xrm-ex/src/XrmEx.js.
  2. Upload XrmEx.js to Dynamics 365:
    • Open Power Apps (or Dynamics 365 Customization) and navigate to Solutions.
    • In your solution, go to Web Resources and click New to add a new web resource.
    • Upload the XrmEx.js file and name it something identifiable, like prefix_/XrmEx.js.
    • Save and publish this web resource to make it available for use in your forms.
      Recommendation: For faster and more convenient web resource management, use the WebResource Manager tool from the XrmToolBox. This tool streamlines uploading, updating, and publishing web resources, saving time during development.
  3. Add XrmEx.js as a Form Library:
    • Open the form where you want to use xrm-ex (e.g., the Contact form).
    • Go to Form Properties and, under Form Libraries, add the XrmEx.js web resource you just uploaded.
    • Ensure it’s included as a library for the form and is set to Load at Runtime.
  4. Add Your Custom Script File:
    • If you’ve created a custom script file (e.g., contactForm.js containing your field and tab definitions), you’ll also need to upload this as a web resource.
    • Repeat the same steps as with XrmEx.js—upload your custom file to the solution, publish it, and add it as a form library.
  5. Configure the OnLoad Event:
    • In Form Properties, go to the Events tab.
    • Under Form Libraries, select your custom script file.
    • Add an OnLoad event and set it to call your OnLoad function (e.g., YourNamespace.Contact.OnLoad).
    • Check the Pass execution context as the first parameter option to ensure that xrm-ex initializes correctly.
  6. Publish and Test the Form:
    • Save and publish all changes to your form.
    • Open the form in Dynamics 365 to test. If everything is set up correctly, xrm-ex should initialize, and you should see any custom functionality you’ve implemented in your script (such as default values or visibility settings) in action.

Conclusion

xrm-ex is a game-changer for Dynamics 365 JavaScript development. By providing a type-safe structure, it brings improved IntelliSense, simplifying event handling, request execution, and attribute control. Whether you're building complex workflows or managing simple form customizations, xrm-ex makes the process easier and more efficient.
Try xrm-ex in your next project and see how much time and effort you can save. Ready to elevate your Dynamics 365 JavaScript development? Head over to the xrm-ex npm page to get started today!

Additional Resources

Example Template

/// <reference path="node_modules/xrm-ex/src/XrmEx.d.ts" />
var YourNamespace = YourNamespace || {};
YourNamespace.Contact = YourNamespace.Contact || {};
(function (Self) {
    class Fields {
        Firstname = new XrmEx.Class.TextField("firstname");
        Customer = new XrmEx.Class.LookupField("parentcustomerid");
        DoNotEmail = new XrmEx.Class.BooleanField("donotemail");
        Birthday = new XrmEx.Class.DateField("birthdate");
        PreferredContactMethod = new XrmEx.Class.OptionsetField(
            "preferredcontactmethodcode",
            {
                Any: 1,
                Email: 2,
                Phone: 3,
                Fax: 4,
                Mail: 5,
            }
        );
    }
    class Tabs {
        General = new XrmEx.Class.Tab("tab1", {
            Section1: new XrmEx.Class.Section("section1"),
            Section2: new XrmEx.Class.Section("section2"),
        });
        Details = new XrmEx.Class.Tab("tab2", {
            Section1: new XrmEx.Class.Section("section1"),
            Section2: new XrmEx.Class.Section("section2"),
        });
    }
    class Grids {
        ContactSubgrid = new XrmEx.Class.GridControl("Test");
    }

    /**@type {Xrm.FormContext}*/ var formContext;
    /**@type {Fields}*/ var fields;
    /**@type {Tabs}*/ var tabs;
    /**@type {Grids}*/ var grids;

    Self.OnLoad = async function OnLoad(executionContext) {
        await Init(executionContext);
        try {
            fields.Firstname.Value = "Joe";
            fields.Firstname.setVisible(true).setDisabled(true).setRequired(false);
            await XrmEx.openAlertDialog("Success", "Xrm works.");
        } catch (error) {
            console.error(error);
            await XrmEx.openAlertDialog("Error", `Error in ${XrmEx.getFunctionName()}\n` + error.message);
        }
    };

    async function Init(executionContext) {
        if (!XrmEx) {
            let errorMessage = "XrmEx is not loaded. Please make sure you have XrmEx.js loaded in your form.";
            console.error(errorMessage);
            await Xrm.Navigation.openAlertDialog({ title: "Error", text: errorMessage });
            return;
        }
        XrmEx.Form.executionContext = executionContext;
        formContext = XrmEx.Form.formContext;
        fields = new Fields();
        tabs = new Tabs();
        grids = new Grids();
        parent.window.XrmEx = XrmEx;
    }
})(YourNamespace.Contact);


Comments