Blog, Development

Development of custom Html/JS Webresources with help of modern frameworks

As you may know, Dialogs have been deprecated and are going away. I have a few customers who’ve asked me to replace their existing dialogs with something that would allow them to get the same result without losses in functionality.

Microsoft recommends 2 techniques to replace Dialogs – BPF and Canvas Apps (embedded or opened by the Url).
BPF just can’t do what I need.
Canvas Apps looks like a good solution but with the ease of customizing, I experienced issues with support and moving Canvas Apps between environments and sending data back to the calling source.

So I decided to choose the third option – the development of HTML/JS Web Resources.

Tools and technologies

Let me explain my approach. I plan to develop rich Html/Js webresources with UCI lookalike controls. That’s why I added “Office UI Fabric” to the list. “Office UI Fabric” is a “React”-based framework – that’s why “React” is in the list as well.

To quickly begin app development, I use the “Create React App” package because it contains the initial version of the application, configured webpack, and much more so there is no need to do a lot of the initial configuration.

Last but not least – “Visual Studio Code” – that’s the editor I use to work with the code for this project.

Initialization of the project

Before initialization of the project, it’s required to install npm package “create-react-app”. In order to do that, I run the following command from the terminal:

Once it is installed, I navigate to the folder of the project and run the following command:

After the project is initialized, I open it using “Open Folder” menu item of VSCode. That’s how the initial structure of the project could look like:

To check that everything works the way it should, I run the following command from the terminal:

As a result, in the browser I see the following application:

Obviously, I don’t need everything that is added to the project by default, so I delete unneeded items and remove all places where those items are referenced. The following screenshot demonstrates the comparison of the project’s structure before and after the “clean-up”:

After all modifications, files look like this:

index.html:

I deleted all of the content from “App.css”. And because of this reason I don’t provide the content of it here.

App.tsx:

index.tsx:

During the run of “npm start” command, a blank browser window is presented. So now, after all preparations have been completed, it’s possible to switch to the fun part that is the development of the application.

In my project, I plan to rely on Office UI Fabric (or Fluent UI how it is called now) so I run the following command to install the required packages:

Scenario

With a click of the ribbon button, the user is presented with a modal dialog window that contains a text and date input control, initial values for fields are passed from the calling script. By click on the “OK” button located at the bottom of the dialog – the window is closed and user inputs are returned to the calling source. If the “Cancel” button is clicked – nothing happens.

Implementation

Let me start from “App.tsx” – the following listings contain all of the code that is required and contains comments (if I missed anything, feel free to ask for clarification in comments):

I used a special CSS class for the “footer” div. Here it is:

The next place I have to modify is the code that “starts” the application – “index.tsx”. Here is the code:

After this change, the App will stop rendering and the browser will display a bunch of errors but never mind – it’s ok – the next tests I will do will be tests from CE.

Build and deployment

To build the project I run the following command:

Once the project is built, a new folder is created in the project’s folder – “build”. Here is what it could look like:

That’s not the perfect folder structure to maintain. In order to change it, it would be required to change webpack config. To do that I open \node_modules\reacts-scripts\config\webpack.config.js and look for the following part of the configuration and comment it out to change the file to:

Also I apply following changes to have consistent naming of css and js parts of the project:

After changes are applied, I run the build command using the following script:

Now “build” folder should look like the following:

The last change in the files I will make before the deployment to CE is the following change in “index.html” from the “build” folder – it’s basically a change of references for .js and .css files from absolute to relative:

Now those 3 files can be imported to CE. I created a solution and added 3 Web Resources to it:

Invoking the Dialog Window

Using the following code, I invoke this dialog from the form or ribbon script. As a parameter for the function I pass in formContext:

Demonstration

The following GIF demonstrates how it would look like if this dialog is called on click of a ribbon button:

36 Comments

    1. The end result will be html/js webresource anyway – cool thing of Office UI Fabric is that developer gets awesome “UCI-lookalike” controls, React allows to concentrate on functionality and not spend a lot of time on data-binding.
      Andrew

  1. Hi Andrew

    Looks nice and I wanted to try it myself.

    However I got struck editing the webpack.config.js as the images are too small for me to see clearly and have probably made a mistake and the build doesn’t produce the files as expected.

    Could you please post larger ones or the before and after text?

    Cheers

    1. Richard,
      Try to zoom in using the browser (Ctrl + Mouse Wheel) – it will be easier to see details of changes.
      Andrew

    1. Dan,
      Your comment and your inspiration is the reason why I keep doing what I was and am doing. Thanks for your warm words!
      Andrew

  2. Hi Andrew, a really great post.
    Just a little comment to polish. There is no need to use this line of code (“window.parent.$(“h1[data-id=’defaultDialogChromeTitle’]”, window.parent.document).html(“My Custom Dialog”);”) to change the web resource Display name. You can just web resource display name in solution to “My Custom Dialog” and it will appear on the modal. Hope it helps and thanks again!

    1. Michael,
      Thanks. I noticed that as well but I haven’t updated the post yet.
      Andrew

  3. HEllo Michael,
    Thanks for the tip!
    I’d actually love some help with configuring Babel and Webpack for a Dynamics CRM 2011,
    if you can post a quick-guide on the hows it would surely by of great help!

    thank you,
    Ofek

    1. Erik,
      I don’t think it could be used within CDS scripts but it could be used in Html/Js Webresources and PCF controls.
      Andrew

  4. Hello Andrew,

    Will this approach works for updating the field of multiple records at once?

    Thanks!

    1. Yasaswini,
      Can you please explain your usecase? I’m not sure I understand how do you plan to use Html/JS webresource in this situation.
      Thanks,
      Andrew

  5. Hi,
    I am trying and stuck at point where it is showing error at $. Could you please tell me what should i do here? installed jquery and added under import statements.

    1. Ramakrishna,
      Did you add //@ts-ignore directive before lines with $ symbol?
      Also there is an alternative way on how it’s possible to close the form/set the label – check the updated post.
      Thanks,
      Andrew

  6. Hi Andrew,
    first of all: thank you for the great post!

    I’m trying to use the same approach in order to have the web resource send back a return value, but no matter what I do the success promise I pass to navigateTo.then() always gets a result containing {returnValue: null}.

    Do you happen to know what could cause such an issue?
    The CRM I’m using is Dynamics 365 9.2.21012.00146, and the app is recognized as Unified Interface.

    Thank you in advance.
    Romeo

    1. Hello Romeo,
      I used that approach literally the last week and the code worked without any issue.
      Here is the “Dialog” side:

      window.returnValue = this.state;
      window.close();

      Here is the “Dialog Invoker” side:

      Xrm.Navigation.navigateTo(dialogParameters, navigationOptions).then(
      function (returnValue) {
      if (!returnValue || !returnValue.returnValue || !returnValue.returnValue.queueId) {
      return;
      }

      var queueId = returnValue.returnValue.queueId;

      Andrew

      1. Hi Andrew,
        ~1 year ago the solution like this was be created.
        but after the last update “9.2.20123.00143” our website doesn’t return anything anymore.
        can you please verify that this still is working ?

        Thanks,
        Andre

        1. Hello Andre,
          It seems that this approach doesn’t work anymore. I will have to update the article and return the approach that used session storage to return results back.
          Andrew

        2. Hi Andrew,
          I am not happy about the normal sessionState, because on some validateion tools of dynamics it’s define as critical error. also on the mobile deveices this could be a problem. maybe the better solution is to use “Microsoft.CIFramework”
          – getSession
          – createSession
          – …

          I will also design try to design a workaround.

          Andre

        3. Andre,
          Please feel free to share once you’re done with the workaround.
          Andrew

        4. Hey,

          running into the same issue, that the value is not passed anymore using window.returnValue.
          Is there a simple workaround yet?

          Thanks!

        5. Serf,
          I updated the code to use sessionStorage to return the response to the calling part.
          Andrew

  7. Well, thank you for the reply, I actually didn’t manage to understand what the problem is and sidestepped the problem by setting/reading the sessionState.

    In the future I hope to have the time to get back to the issue and understand what the issue was in case somebody stumbles in this article while investigating the same issue.

    Keep up the good work 🙂

    Romeo

  8. Hi Andre/Andrew,
    Thanks for your comment and solution. I have followed the same article and implemented it was working fine.
    But now, I am also facing the above issue that, we are always getting returnValue as “null”

    could you please help us with a workaround if any?

    1. Hello Vivek,
      I will update the post to use the solution that I used before – usage of the session storage to pass the result to the calling code.
      Andrew

      1. Hi Andrew,

        I have used the session Storage to pass the result in the calling code. It’s working as expected.
        But I would like to know if there is any other approach because I have seen that Andre Grumbach mentioned that it will create a problem if we use this approach.

        Vivek Mugundu.

Leave a Reply to Andrew Butenko Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.