Building Blocks

The building blocks below help you develop advanced web-based customizations for CRM using non-Visual Studio based tools.

Editors

Editors are a very personal choice for a developer. Popular editors include sublime, idea, atom, vim and emacs. Recently, Visual Studio Code came onto the scene and has become popular.

For development in this book, I am using emacs. Emacs has been around awhile and is written in emacs lisp. Although the UI lacks some polish in places, emacs allows me edit most source-code based projects easily and rarely force me to use the mouse so my hands stay on the keyboard.

To edit javascript projects for CRM in emacs, I use the following packages:

  • web-mode: javascript/html editing mode

  • flycheck: javascript syntax checking and additional javascript editing support

  • supporting modes:

    • helm

    • projectile

emacs has a packaging system, like all the other editors, that allows me to easily include these packages. I also perform some customization using emacs lisp code, which is quite normal for emacs users. This is equivalent to modifying javascript configuration files in other editors.

In my init.el I have:

(use-package flycheck
  :ensure t
  :init (progn
      (global-flycheck-mode)
      (flycheck-add-mode 'javascript-eslint 'web-mode)))

(use-package web-mode
  :ensure t
  :init (add-hook 'html-mode-hook 'emmet-mode)
  :mode ("\\.\\(html\\|htm\\)$" . web-mode)
  :mode
  (("\\.phtml\\'" . web-mode)
  ("\\.tpl\\.php\\'" . web-mode)
  ("\\.[gj]sp\\'" . web-mode)
  ("\\.as[cp]x\\'" . web-mode)
  ("\\.erb\\'" . web-mode)
  ("\\.js[x]\\'" . web-mode)
  ("\\.mustache\\'" . web-mode)
  ("\\.djhtml\\'" . web-mode)
  ("\\.html?\\'" . web-mode)
  ("\\.css?\\'" . web-mode))
   :config
  (progn
    (setq web-mode-content-types-alist
          '(("jsx" . "\\.js[x]?\\'")))
    (setq web-mode-enable-auto-quoting nil)))

I'm not showing the other packages which also have entries in init.el but the above ones are the critical ones. I found these settings by searching on google and copying them into my init.el file.

I also use npm, so I customize the internal emacs PATH to include node_modules/.bin automatically so I do need to install my required node commands globally e.g. webpack. When I am on the shell command line I add node_modules/.bin to the command I want to run but its rare that I run node module based commands manually. This approach keeps my shell PATH clean and avoids global node/npm packages--both of which help me create reproducible builds.

$ cat .dir-locals.el                                                                                         
;; Add node_modules/.bin to the emacs path so
;; it finds locally installed packages.

((nil . ((eval . (progn
                   (add-to-list 'exec-path (concat (locate-dominating-file default-directory ".dir-locals.el") "node_modules/.bin/")))))))

If you search the web, you can find more details on how to set up emacs for javascript development.

With this setup, I get real-time syntax checking for my javascript using eslint, another node program.

I tend to break up my emacs window into 4-8 panes across 3 OS windows with the syntax checker error window below, but tastes vary.

Visual Studio has great typescript support which means refactoring and documentation tooltips work great. I need alot of windows open in multiple configurations so emacs works for me good enough.

Build tools

There are many, many different build tools covering different aspects of the build process. My stack includes:

  • eslint: For syntax checking.

  • babel: Transpiles newer javascript standards to older standards.

  • webpack: Loader, packer, dev http server, you name it

Lately, webpack has become popular and replaces grunt and bower, among others. A good tutorial is here.

I also use linting, to check javascript syntax. It is not as nice as a compiler that compile typed programming languages, but it does help. I do not use typeclass although I probably should. I did not want to fool around with the .ts files. For linting, I use the following configuration in .eslintrc:

{
  "plugins": [
    "react"
  ],
  "parserOptions": {
    "ecmaVersion": 7,
    "sourceType": "module",
    "ecmaFeatures": {
      "jsx": true,
      "experimentalObjectRestSpread": true
    }
  },
  "env": { 
    "browser": true,
    "node": true
  }
}

My babel settings are sometimes kept inline with webpack.config.js but they can be set in the package.json or separately as a .babelrc file. Here's my webpack.config.js babel-loader settings:

 ,{
    test: /\.jsx?$/,
    exclude: /(node_modules)/,
    use: { 
       loader: 'babel-loader',
       options: {
         presets: ['env', 'react'],
         plugins: ["transform-object-rest-spread"]
       }
     }
},

which says to use the babel-loader to load .js and .jsx files into webpack for processing and to use various presets (bundled transpiler processors), as well.

I'll cover webpack configuration later as its more complex, but one part of webpack is the webpack dev server. An http server that can server up your resources, proxy requests, and do a variety of other useful 'web serving" tasks you need during web application development. CRM requires https and since we will describe how we do hot reloads later, we need to enable https on the webpack dev server. In the webpack configuration file, this is all you need:

   },
    devServer: {
    https: true
    }

Note that you may need to goto to your https://localhost:8080 and accept the certificate and possible turn off any CORS browser plugins to ensure that you can use the dev server to get hot reloads to work.

Web frameworks such as React, Redux, etc.

Web frameworks help you manage the complexity of your solution. There are numerous javascript frameworks you can use, some of them lightweight like backbone, and some of them more heavy (in terms of runtime size and functionality) like react. You need to select a framework that makes sense for your project.

One framework that we will use is react. React was developed by facebook to improve the development experience and tame some of the complexities when developing web-based applications. React is very popular, although there are now alternatives that are quite similar and "lighterweight" than react. However, with facebook's backing, react appears to be here to stay. Angular, backed by google, is another popular framework although angular is more ambitious in scope than react.

React

React is the functionally-leaning, view framework from facebook. React comes in two flavors, reactjs and react.native. React.native is the implementation of react concepts using javascript and native libraries so that your applications appear native. reactjs is used for web/browser programming. The focus for CRM UI is reactjs ("react").

React combines javascrpt and "HTML" together in the same file to specify your UI. The fundamental idea is that the state of your component is capture in a state object and is considered immutable. You can only set the entire state object when you want to change state. The UI is automatically rendered when the state changes. The UI is specified in an HTML looking syntax called jsx. Each component renders its UI by returning jsx from the render() function. React identifies only the changes in the jsx and those changes are reflected on the screen by manipulating the DOM. Because changing the DOM is very time-consuming and intensive, minimizing DOM changes is critical. React provides a way to minimize changes through its "virtual DOM."

When developing CRM UI Web Resources, you always need a way to render the UI components that you wish to make presentable to the user. You can use any web ui framework, such as backbone. You can also use react directly.

Redux

Redux manages state for your application and allows UI components to listen for changes in the state, then change the UI to reflect those changes.

Redux is message based, meaning the only way you change the application state is by sending a message to the "store":

const store = Redux.createStore(...);
//...
store.dispatch({type: 'LOAD_CONTACTS'});

The example above causes the store to indicate that you are loading CRM Contact records. You have to use the CRM Web API to actually perform the data load.

By notifying the store that you are loading contacts, you give listening components the chance to update their UI. For example, if you are using react, like are in this book:

class MyComponent extends React.Component {
   componentDidMount() {
     this.context.store.subscribe(() => this.forceUpdate());
   }
   // ...
   render() {
     // Use the state to render
     return <p>this.context.store.getState().loadingText<p>; 
   }
}

All of this is transparent and separate from CRM so you can use them in your Web Resources. There are easier ways to create the above component without the React.Component class boilerplate (see react-redux's api). react-redux is a redux extension for react that puts the store into the react context, as you saw above when the store was access using this.context.store.

You can imagine that modifying the immutable application state make take many message types and that's true. You have to create some boilerplate for the messages when using redux. However, the benefit is that you have state management centralized in your store and you typically only need one store per application.

There are many points of view around redux and whether it adds complexity or removes it or concentrates it and whether that's good or bad in different situations. However, for our purposes it should be fine. Some people like to add layers on redux to make it more palatable for certain situations. You can read docker's redux manual here which covers a variety of different techniques you can apply to your application. For example, you could use reselect which allows you to create derived data to pass to components when the redux state changes or allows you to only render when the state has changed in part of the state tree. It's easy to see that these are just layers on top of plain redux for special situations.

React+Redux for CRM

Together react and redux allow you to create new UI components that are provided through the CRM Web Resources infrastructure. Since CRM Web Resources are just web files such as css, javascript or mages, you need to package your UI files together in a way that allows you to deploy them to CRM UI. Webpack allows you to package your content into a small set of files that you can then load as a Web Resource manually or through a loadable Solution.

However, there is still some work needed to glue together the Web Resource model with the react+redux model and allow you to get access to CRM resources. For example, when you edit a Contact record in a Form, your component will probably need to know what the entity id is in order to allow you to display some relevant Contact data.

Stuff CRM Already Uses

Dynamics CRM web ui already uses several frameworks underneath the hood. Generally, you count on jquery being present on most web ui pages. It can be accessed in your code using:

var $ = $ || parent.$;

Technically, MS reserves the right to change things that are not in their published API, which is very fair. Their published API is fairly well published and involves accessing Xrm and more specfically Xrm.Page which the page context. The global Xrm context is really available at Xrm.context.

Should you count on jquery being present? I think it is fairy reasonable to do so although it is easy enough to include a jquery web resource in your Web Resource. Since Web Resources are loaded into their own iframe, you will not overwrite the MS jquery.

Having said all these things about jquery, react+redux should remove some of the need to use jquery. Where you will find jquery useful is when you need to hack into the DOM or other parts of the CRM generated page that are not running react.

Access to Xrm

Xrm is a key resource for accessing form content. The latest release updates several APIs. The release also includes a new "unified" UI that has a different load order model than the "Web" UI. Hence, you must be much more careful about obtaining access to Xrm. You can always access the Global Context through the .aspx load, but if you want general access to the form context (i.e. Xrm.Page) you may have to wait for it.

For example, in a HTML file, you may need to add some boilerplate:

function waitForIt(okToProceed, cb) { 
             var cancellable = setInterval(function() {
                 var proceed = okToProceed && okToProceed()
                 if(proceed) { 
                     clearInterval(cancellable)
                     cb()
                 }
             }, 500)
}

function check() { return typeof window.parent.Xrm !== "undefined" }

function init() {
  ...
  waitForIt(check, initStuff)
  ...
}
...
window.addEventListener("load", init)

To wait for Xrm to be available, we can also just use a promise with an internal polling mechanism:

function getXrmP(maxIter, delta) {
             delta = delta || 500
             return new Promise(function(resolve, reject) { 
                 var count=0
                 var cancellable = setInterval(function() {
                     if(maxIter && count > maxIter) reject(null)
                     count = count + 1
                     var proceed = typeof window.parent.Xrm !== "undefined"
                     if(proceed) { 
                         clearInterval(cancellable)
                         resolve(window.parent.Xrm)
                     }
                 }, delta)
             })
         }

Now, if you write an HTML file or need to access properties on Xrm, do it inside the callback:

function run(el) {
  getXrmP().then(xrm => {
    ...render your react nodes into el...
  })

This is much simpler and the right way to do it since there is no guarantee on loading completion time even though window.parent.Xrm works most of the time. However, encapsulating this in an asynchronous function is much more future proof against weird internet loading issues.

Last updated