Press "Enter" to skip to content

ReactJS as Dynamics 365 CE Web Resource

Christoph Stiedl 2

In this article we would like to show how to kick start a custom ReactJS UI implementation within Dynamics 365 Customer Engagement using an existing ReactJS template.

In a nutshell

We licensed the Light Bootstrap Dashboard PRO React Template by Creative Tim for our customer and integrated the template into Dynamics 365 CE to develop a custom UI upon that using Web Resources (IFRAME). In this post we’d like to show the first steps how to get started and set that up.

Background

The user interface of Dynamics 365 CE improves steadily. Especially with the release of the Unified Interface. Nevertheless, there are moments when you encounter the limits of the Dynamics 365 CE UI capabilities and you need to think about different means how to provide great usability. One of the means, is the now available possibility to develop custom Canvas Apps. Another one is the future possibility of the Custom Control Framework (which is not yet available, but will be soon). In the meantime, the best option to provide a custom user interface within Dynamics 365 CE is a custom UI implementation using frameworks like ReactJS or Angular. For both options we have implemented several implementations. For ReactJS we would hereby like to provide a simple guide how to get that started.

Hosting of the UI

For every custom implementation of a web interface you have to think about hosting. Using SPA (single page application) design you have to worry about two different questions:

  • A server that provides download of the HTML and JavaScript files
  • A back-end to provide an API that exposes the data

Regarding the hosting of your client side resources (HTML, CSS, JavaScript, images) in combination with Dynamics 365 CE exist basically the following two options:

  • Using Dynamics 365 CE as your host (meaning you deploy the HTML, CSS, images and JavaScripts as Web Resources to Dynamics 365 CE)
  • Hosting your HTML and Web Resources on a custom server (e.g. a custom .NET Web App that is hosted on Azure or on premise within IIS). You’d like to do that when your user interface should be also available outside of Dynamics 365 CE.

Regarding the back-end you could:

  • Use the web api of Dynamics 365 CE (if you access Dynamics 365 CE data only) and expose custom actions (also exposed over the web api)
  • Additionally expose a custom API using .NET Web API hosted on Azure for example (secured e.g. with OAUTH2 using Azure AD and ADAL.js on client side)

In this article we would like to focus on the ReactJS part. Due to this we will show how to host ReactJS as Web Resource within Dynamics 365 CE (you could then use Xrm Tooling to access the Dynamics 365 Web Api from within the ReactJS application). In a future post we will show to put ADAL.js on top of it to handle authentication against a custom .NET Web API using OAUTH2.

Getting started

After licensing the template and downloading and extracting it you should end up with a folder structure as following:

First thing to mention here is that the template we chose was created with one of the most popular starter kits, the Create React App starter kit.

Looking at the package.json you’ll see that it looks quite simple. Well, that’s actually why the Create React App starter kit is so popular. Most of the logic is handled actually within the react-scripts and is not visible for the developer. If you want to see the details, you could create a copy and eject to see the details. We would recommend that only on a copy, since it’s a one way action where you cannot get back (except with the help of source control and undo). Ejecting from the simple version of the Create React App starter kit should be the last option if you really cannot handle something . In this guide we will not eject and stay on the simple version that uses react-scripts.

{
  "name": "light-bootstrap-dashboard-react",
  "version": "1.2.0",
  "private": true,
  "dependencies": {
    "bootstrap": "3.3.7",
    "chartist": "0.10.1",
    "node-sass": "4.6.1",
    "node-sass-chokidar": "0.0.3",
    "npm-run-all": "4.1.2",
    "react": "16.2.0",
    "react-bootstrap": "0.32.1",
    "react-chartist": "0.13.1",
    "react-dom": "16.2.0",
    "react-google-maps": "9.4.5",
    "react-notification-system": "0.2.17",
    "react-router-dom": "4.2.2",
    "react-scripts": "1.1.1"
  },
  "scripts": {
    "build-css": "node-sass-chokidar --include-path ./src --include-path ./node_modules src/ -o src/",
    "watch-css": "npm run build-css && node-sass-chokidar --include-path ./src --include-path ./node_modules src/ -o src/ --watch --recursive",
    "start-js": "react-scripts start",
    "start": "npm-run-all -p watch-css start-js",
    "build": "npm run build-css && react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
}

To run the ReactJS app you will need to install NodeJS and Python (add python executable to path or check it in the installer). Afterwards run the following script to install the necessary modules.

npm install

If you encounter problems during installation, have a look here and here.

Having the dependencies downloaded and installed, you can start the app:

npm run start

This will start the webpack server and should bring up the following browser window with the following url: http://localhost:3000/#/dashboard

Integration into Dynamics 365 CE

When working locally, npm run start will start the webpack server and this will serve the files from memory. For deploying the first draft of the template to Dynamics 365 CE we first need to generate the build files so we can then try to upload them to Dynamics as web resource. To do that, execute the following command:

npm run build

This will generate bundled and minified JavaScript and CSS files as well as media file in the folder build/static.

  • build\index.html
  • build\static\js\main.fd363da8.js
  • build\static\js\main.fd363da8.js.map
  • build\static\media\Peicon7stroke.01798bc1.ttf
  • build\static\media\Peicon7stroke.b38ef310.woff
  • build\static\css\main.1cfbe954.css
  • build\static\css\main.1cfbe954.css.map

Challenges

Looking at the generated files you might notice that a random build number is used for the output files. Moreover some icon files are used, where we currently will only handle the Pe-icon-7 files as an example.

To upload those files to Dynamics 365 CE we would actually need something more or less in the form of the following (depending on the tooling). In our case, we are using Microsoft Dynamics 365 Developer Toolkit at this customer. You could also upload your resources using spkl as we do at other projects. “pre” represents some prefix for your solution publisher. Depending on your tooling you could replace the “_” with “\” and a folder structure (we do so in Visual Studio in the properties of the files, see below). Please note that for some files we had to add the the extension “.js” or “.css“. To upload those files we have to trick Dynamics and make it think it was actually a .js or .css file. Furthermore look closely, that for the Pe-icon-7 files we had to replace “” with “_“. All due to some Dynamics 365 CE requirements for the file names that are allowed for web resources and that can be uploaded.

  • WebResources\Web Page (HTML)\pre_sample_index.html
  • WebResources\Script (JScript)\pre_sample_static_js_bundle.js
  • WebResources\Script (JScript)\pre_sample_static_js_bundle.js.map.js
  • WebResources\Script (JScript)\pre_sample_static_media_Pe_icon_7_stroke.01798bc1.ttf.js
  • WebResources\Script (JScript)\pre_sample_static_media_Pe_icon_7_stroke.b38ef310.woff.js
  • WebResources\Style Sheet (CSS)\pre_sample_static_css_styles.css
  • WebResources\Style Sheet (CSS)\pre_sample_static_css_styles.css.map.css

Using the Microsoft Dynamics 365 Developer Toolkit the files are then represented as following to form a nice folder structure within Dynamics 365 CE:

So the challenges are:

  • a random build number that makes deployment to a web resource tricky
  • the generated .js file and .css file contain references to the corresponding .map files (to see that open the files and look at the end of the file) also containing the random build number
  • we have to change the file extension to be able to upload the resources to Dynamics 365 CE (at least when we do it manually)
  • The file name of the icon files would need to be changed, so also the references to those files within the icon font .css file

The missing link

Looking at what the Create React App currently provides and what we would actually need, a simple script would be great that would take care of those transformations. And that is actually what we provide here by first changing the package.json the following way:

"scripts": {     
"copyCSSFiles": "cpx \"build/static/css/*.*\" \"../WebResources/Style Sheet (CSS)\"",
"copyJSFiles": "cpx \"build/static/js/*.*\" \"../WebResources/Script (JScript)\"",
"processBuildFilesForDeployment": "node
src/sample/processBuildFilesForDeployment.js",
"build-css": "node-sass-chokidar --include-path ./src --include-path ./node_modules
src/ -o src/",
"watch-css": "npm run build-css && node-sass-chokidar --include-path ./src --
include-path ./node_modules src/ -o src/ --watch --recursive",
"start-js": "react-scripts start",
"start": "npm-run-all -p watch-css start-js",
"build": "npm run build-css && react-scripts build && npm run
processBuildFilesForDeployment && npm run copyJSFiles && npm run copyCSSFiles",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}

For the build command, we added the execution of our own scrips after the build, that first process the output files (processBuildFilesForDeployment) and then copy over the processed files to the WebResources folder that we use for deployment to Dynamics 365 CE. Let’s have a closer look to this custom processBuildFilesForDeployment.js then.

var glob = require("glob"),
    path = require("path"),
    fs = require("fs");

renameBundleJS();
renameBundleJSMap();
renameBundleCSS();
renameBundleCSSMap();

function renameBundleJS() {
  glob(__dirname + "/../../build/static/js/*.js", function(err, files) {
    files.forEach(function(file) {
      // Change reference to map file
      var data = fs.readFileSync(file, 'utf8');
      var result = data.replace(/# sourceMappingURL=.*$/g, '# sourceMappingURL=bundle.js.map');
      fs.writeFileSync(file, result, 'utf8');

      // rename file
      var dir = path.dirname(file);
      var filename = path.basename(file);
      fs.renameSync(file, dir + "/pre_sample_static_js_bundle.js");
    });
  });
}

function renameBundleJSMap() {
  glob(__dirname + "/../../build/static/js/*.js.map", function(err, files) {
    files.forEach(function(file) {
      // rename map file
      var dir = path.dirname(file);
      var filename = path.basename(file);
      fs.renameSync(file, dir + "/pre_sample_static_js_bundle.js.map.js");
    });
  });
}

function renameBundleCSS() {
  glob(__dirname + "/../../build/static/css/*.css", function(err, files) {
    files.forEach(function(file) {
      // change reference to map file and also replace reference to fonts 
      // file that we had to rename with _ instead of - in the web resource.
      var data = fs.readFileSync(file, 'utf8');
      var result = data.replace(/# sourceMappingURL=.*$/g, '# sourceMappingURL=styles.css.map*/');
      result = result.replace(/Pe-icon-7-stroke/g, 'Pe_icon_7_stroke');		
      fs.writeFileSync(file, result, 'utf8');

      // rename file
      var dir = path.dirname(file);
      var filename = path.basename(file);
      fs.renameSync(file, dir + "/pre_sample_static_css_styles.css");
  	});
  });
}

function renameBundleCSSMap() {
  glob(__dirname + "/../../build/static/css/*.css.map", function(err, files) {
    files.forEach(function(file) {
      // rename css file
      var dir = path.dirname(file);
      var filename = path.basename(file);
      fs.renameSync(file, dir + "/pre_sample_static_css_styles.css.map.css");
  	});
  });
}

The code above takes care of the following

  • changing source map references within the bundled .js and .css file
  • renaming the bundled .js and .css files to get rid of the random build number
  • adding the necessary file endings that we need for upload to Dynamics 365 CE
  • Changing the reference for the icon files since in the file name we replaced ‘-‘ with ‘_’

In the scripts “copyJSFiles” and “copyCSSFiles” within the package.json we copy over the resulting files. Since the icons files (Pe-icon-7) do not change very frequently we just copy them over into the Web Resources folder once manually and rename them to “Pe_icon_7“.

Entry point

The part missing until now is the index.html. For this we also just create a static version within the Web Resources folder (that is not copied over automatically). We took the index.html from ReactJS as a reference and then we changed the references to the .css and .js files. The outcome looks similar to this (simplified version):

<html>
<head>
    ...
    <link href="./static/css/styles.css" rel="stylesheet">
</head>
<body>
    <div id="root"></div>	  
    <script type="text/javascript" src="./static/js/bundle.js"></script>
</body>
</html>

Putting it all together

  1. Execute “npm run start” to start the local webpack server for development
  2. Upload at least the index.html as web resource once, so you can include it as a web resource (IFRAME) on some form
  3. Use Fiddler and AutoResponder for local development (this redirects your browser requests that would normally be served by the Dynamics 365 CE server to your local webpack server). This way you can open Dynamics 365 CE user interface, which will load the form, but then the IFRAME contents (so your ReactJS App) is served from your local development server. Since for the browser it appears as if the files were served by the Dynamics 365 CE server, you can even access the Dynamics 365 CE Web Api without any authentication (since cookies are automatically passed, as for the browser it appears as the Dynamics domain)
  4. When you are done, execute “npm run build“. This will create the minified bundles. Source map references will be exchanged, font icon file references changed, files renamed and copied over to the Web Resources folder.
  5. Then deploy and publish the just built bundled and minified files to the Dynamics 365 CE server

The rest is then ReactJS web development and design, accessing probably the Dynamics 365 CE Web Api or some custom REST Api to retrieve some data, transform it and present it nicely.

Summary

We showed how to use a ReactJS template (one that is implemented by means of the Create React App starter kit) and how to modify it just a little bit to make it Dynamics 365 CE Web Resource deployment ready. This way no manual modification is anymore necessary and the development and build process is actually quite smooth and time saving. Starting with the licensing of an existing template saves lot of manual design and coding time.

  1. Nick Patel Nick Patel

    Christoph Stiedl, thanks for putting this article together. I have a questio nregarding the debugging. React project is easy to build – debug using vscode code. But when we deploy these files into CRM, how do we able to debug the files in Browser? these files will be hard to debug as the original reac files will be transformed into much differetn js files?
    can you please provide some debugging approach that should help development right from CRM?

    • Christoph Stiedl Christoph Stiedl

      Hi Nick,

      you can also upload the mapping file. Actually we are already preparing the correct article above (see # sourceMappingURL above). So basically you need to do two things. First of all, you need the correct mapping reference inside your bundled JS file. This we already prepare with the java script task above. So we rename the map file and also change the reference inside the bundled JS file. And most important, you also need to upload that as web resource to CRM.

      Once in the debugger, just check the reference in the bundled JS if that one is correct and in the network resources also try open the map file link if it is really loading and found.

      For local debugging, what works best is using Fiddler. So you can upload all stuff to CRM and then redirect your browser request with fiddler to load the JS and map file from your local drive instead as from the CRM server. This makes it much easier to develop and later on also debug.

      Let me know if that helps or if you need further assistance on that.

      Regards,

      Christoph

Leave a Reply

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