Microsoft Dynamics 365, a popular enterprise business solution designed to manage business processes, has a plethora of features that fulfill most business needs. But because every business is different, each enterprise generally requires customization. Fortunately, the customizations are straightforward, and can be done through plugins, actions, and web resources.

If you’d like to watch a video for this process, skip to the bottom.

In this blog, we’ll look at using web resources to extend Microsoft Dynamics 365 to create a great user experience using client-side technologies like JavaScript and HTML5. Specifically, we’ll create an account dashboard.

Dashboard01

Since UI and UX are where Wijmo 5’s JavaScript controls really shine, and you can use them in Dynamics 365 to add functionality, improve the style and create a truly superb client relationship management system.

Here are a few things we can do with Wijmo 5 and Dynamics 365:

  1. Display entity records in FlexGrid and perform Excel-like filtering, sorting, and grouping and display hierarchical data.
  2. Represent data in pie chart, bar chart, radar or a linear/radial/bullet graph
  3. Analyze and drill down data using Wijmo OLAP control
  4. Use custom input elements like calendar, autocomplete, multi-select, and drop-down

Let’s get started.

Create an Account Dashboard Using a JavaScript Grid and Chart

First we’ll create a basic accounts dashboard using FlexGrid and FlexChart. As always, we’ll start with a list of requirements. The dashboard needs to:

  • Fetch data from an account entity and display it in the FlexGrid. Features include:
    • Sorting
    • Grouping
    • Filtering
  • Represent data from account records in a pie chart so we can see a pie graph of countries and their accounts
  • Users should be able to update records on FlexGrid, which should update the entity record in the CRM.

Creating the dashboard is easy. First we’ll write the HTML and JavaScript code that uploads data to the CRM as web resources. In our example, we’re using AngularJS as our framework, but Wijmo’s controls allow you to easily substitute a chosen framework with Pure JS, React, Angular v2, or VueJS.

Step 1: Creating the HTML Page

 

Since our HTML page is going to need a FlexGrid and FlexChart to display the data. We are going to add them First and then move to add the JS code later. We have also added FlexGrid filter directive and Group Panel to provide Filtering and Grouping features.

AccountDetailsWijmoView.html

<html ng-app="crmApp">
<head>
    <title>Using Wijmo with MSCRM </title>
    <script src="ClientGlobalContext.js.aspx"></script>
    <!-- AngularJS files  -->
    <script src="gc_angular.min.js" type="text/javascript"></script>
    <script src="gc_angularresource.min.js"></script>
    <!--  Other Framework files & Bootstrap  -->
    <script src="gc_jquery1.9.1.js"></script>
    <link rel="stylesheet" href="gc_bootstrap.min.css">
    <script src="gc_fontawesome.js"></script>
    <script src="gc_bootstrap.min.js"></script>
    <!-- Wijmo 5 Dependencies -->
    <link href="gc_wijmo.min.css" rel="stylesheet" />
    <script src="gc_wijmo.min.js"></script>
    <script src="gc_wijmo.chart.min.js"></script>
    <script src="gc_wijmo.grid.min.js"></script>
    <script src="gc_wijmo.input.min.js"></script>
    <script src="gc_wijmo.grid.filter.min.js" type="text/javascript"></script>
    <script src="gc_wijmo.grid.grouppanel.min.js" type="text/javascript"></script>
    <script src="gc_wijmo.angular.min.js"></script>
    <!-- Controller file -->
    <script src="gc_AccountDetailsController.js" type="text/javascript"></script>
</head>
<body onload="window.resizeTo(1400,800)" ng-controller="AppController" style="word-wrap: break-word;">
    <div class="container-fluid">
        <div class="panel panel-primary">
            <div class="panel-heading">
                <h3 class="panel-title">
                    <i class="fa fa-bookmark" aria-hidden="true"></i> CRM Accounts DashBoard
                </h3>
            </div>
            <div class="panel-body">
                <div class="row">
                    <div class="col-xs-8 col-md-8">
                        <!-- Group Panel to let users drag Columns and group data based on them -->
                        <wj-group-panel grid="flex" placeholder="Drag columns here to create groups." style="height:60px;width:800px"></wj-group-panel>
                         <!-- This is our FlexGrid used to Populate data from CRM -->
                        <wj-flex-grid control="flex" items-source="data" allow-add-new="true" style="height:400px;width:800px;margin-top:10px">
                            <wj-flex-grid-filter></wj-flex-grid-filter>
                            <wj-flex-grid-column header="Account Name" binding="name"></wj-flex-grid-column>
                            <wj-flex-grid-column header="Street Address" binding="address1_line1"></wj-flex-grid-column>
                            <wj-flex-grid-column header="City" binding="address1_city"></wj-flex-grid-column>
                            <wj-flex-grid-column header="Postal Code" binding="address1_postalcode"></wj-flex-grid-column>
                            <wj-flex-grid-column header="Country" binding="address1_country"></wj-flex-grid-column>
                            <wj-flex-grid-column header="Call Allowed" binding="donotphone"></wj-flex-grid-column>
                        </wj-flex-grid>
                    </div>
                    <div class="col-xs-4 col-md-4">
                        <!-- This is our FlexPie used to show Countries of Accounts -->
                        <wj-flex-pie items-source="countriesofAccountCollection" binding="value" binding-name="name" selection-mode="Point" header="Countries of Account" selected-item-offset=0.10>
                        </wj-flex-pie>
                    </div>
                </div>
                <a style="margin-top:10px" class="btn btn-success btn-lg btn-block" ng-click="UpdateRecords()" ng-disabled="data.itemsEdited.length==0" role="button"><i class="fa fa-upload" aria-hidden="true"></i> Update ({{data.itemsEdited.length}} records need to be updated) </a>
            </div>
        </div>
    </div>
</body>
</html>

Step 2: Add the JavaScript code and Perform Read and Write Operations on Accounts Entity

We’re using Dynamic CRM’s Web API to read and write data on Accounts Entity. Data fetched from Web API will be displayed in the FlexGrid, and any edits in the FlexGrid will be updated back to Dynamics 365. We’re also creating a function to analyze data and show the resulting data in a FlexChart.

AccountDetailsController.js

// JavaScript source code
// JavaScript source code
var CrmApp = angular.module('crmApp', ['wj']);
// Data Service to make an WebAPI call to CRM
CrmApp.service('DataService', function ($q) {
    this.retrieveRecords = function () {
        return $q(function (resolve, reject) {
            var clientURL = Xrm.Page.context.getClientUrl();
            var req = new XMLHttpRequest()
            var query = "/api/data/v8.0/accounts?$select=name,address1_country,address1_composite,address1_city,address1_postalcode,donotphone,paymenttermscode&$top=100";
            req.open("GET", encodeURI(clientURL + query), true);
            req.setRequestHeader("Accept", "application/json");
            req.setRequestHeader("Content-Type", "application/json;charset=utf-8");
            req.setRequestHeader("OData-MaxVersion", "4.0");
            req.setRequestHeader("OData-Version", "4.0");
            req.onreadystatechange = function () {
                if (this.readyState == 4) {
                    req.onreadystatechange = null;
                    if (this.status == 200) {
                        var data = JSON.parse(this.response);
                        if (data && data.value) {
                            resolve(data.value);
                        }
                    }
                    else {
                        var error = JSON.parse(this.response).error;
                        reject(error);
                    }
                }
            };
            req.send(null);
        });
    }
    // Web Api call to Update Accounts
    this.updateRecord = function (accountId, record) {
        return $q(function (resolve, reject) {
            var clientURL = Xrm.Page.context.getClientUrl();
            var req = new XMLHttpRequest()
            req.open("PATCH", clientURL + "/api/data/v8.0/accounts(" + accountId + ")", true);
            req.setRequestHeader("Accept", "application/json");
            req.setRequestHeader("Content-Type", "application/json;charset=utf-8");
            req.setRequestHeader("OData-MaxVersion", "4.0");
            req.setRequestHeader("OData-Version", "4.0");
            req.onreadystatechange = function () {
                if (this.readyState == 4) {
                    req.onreadystatechange = null;
                    if (this.status == 204) {
                        resolve("ok");
                    }
                    else {
                        var error = JSON.parse(this.response).error;
                        reject(error);
                    }
                }
            };
            req.send(JSON.stringify(record));
        });
    }
});
/// create a controller for the page, and inject our DynamicsCRMService into it
CrmApp.controller('AppController', function ($scope, DataService, $q, $window, $timeout) {
    // Get Data for Accounts
    var rawData = [];
    //Get Data for Account Records
    $scope.GetData = function () {
        var promise = DataService.retrieveRecords();
        promise.then(function (response) {
            rawData = response;
            $scope.data = new wijmo.collections.CollectionView(response);
            $scope.data.trackChanges = true;
            $scope.AnalyzeAccounts();
        }, function (reason) {
            alert(reason)
        });
    }
    // Create Data for Country Analysis and Show in Pie Chart
    $scope.AnalyzeAccounts = function () {
        var countriesofAccount = [];
        var unsortedCountriesofAccount = rawData.reduce(function (sums, entry) {
            sums[entry.address1_country] = (sums[entry.address1_country] || 0) + 1;
            return sums;
        }, {});
        angular.forEach(unsortedCountriesofAccount, function (value, key) {
            var countriesData = {
                name: key,
                value: value
            };
            countriesofAccount.push(countriesData);
        })
        $scope.countriesofAccountCollection = new wijmo.collections.CollectionView(countriesofAccount);
    }
    // Udpate Account Edits to CRM
    $scope.UpdateRecords = function () {
        var defer = $q.defer();
        var promises = [];
        var edittedItems = $scope.data.itemsEdited;
        angular.forEach(edittedItems, function (item) {
            var account = {
                name: item.name,
                address1_line1: item.address1_line1,
                address1_country: item.address1_country,
                address1_city: item.address1_city,
                address1_postalcode: item.address1_postalcode,
                donotphone: item.donotphone,
                paymenttermscode: item.paymenttermscode
            };
            promises.push(DataService.updateRecord(item.accountid, account));
        })
        $q.all(promises).then($scope.GetData());
    }
    $scope.GetData();
});

This is all the code we need to create our Account Dashboard with Wijmo 5
controls!

Note: we need to provide relative URLs for our dependencies in the HTML page, as they also need to be uploaded as Web Resources. Uploading the dependencies is a one-time process, and once done, we can re-use the dependencies in other Web Resources as well.

Now we’ll look into the second half of the process: deploying the code to Dynamics 365. Once we publish the files to the CRM, our Account Dashboard will be nearly complete.

Step 3: Deploying Code & Dependencies to CRM

Once our JavaScript and HTML5 code is ready, we’ll publish them to Dynamics 365 with the required dependencies (Wijmo, Angular, JQuery etc.). Please note that name and relative path of dependencies must match with the paths that have been specified in AccountDetailsWijmoView.html.

The best practice is to create a new solution and upload the files there. (You can read about how to create a new solution here.)

We’ve already created a new solution and have uploaded the mentioned files as Web Resources. Here are the steps:

3.1 Navigate to the Settings Page

02-031-magnify

3.2 Select the Solution to Upload Resources

02-032-Resized

3.3 Upload the Files as Web Resources.

If you would like to know how to upload Web Resources, then please visit here.

02-033-Callouts

Step 4: The Entry Point

Take note of the URL field of AccountDetailsWijmoView.html. This serves as the entry point for the Account Dashboard.
In our sample, the entry point is: https://demojs.crm8.dynamics.com//WebResources/gc_AccountDetailsWijmoView.html

02-034-Resized

Step 5: Adding a Ribbon Button using Ribbon Workbench & Ribbon Set Up

Let’s add an Analyze Ribbon Button on the accounts home page. When we click it, we’ll see the Account Dashboard. It’s recommended to use the Ribbon Workbench to add ribbon buttons and associate commands with them. (If we wanted, we could open the entry point URL directly in our Dynamics 365 instance, and it would work just fine.)

5.1 Set up Ribbon Button

05-01-resized

5.2 Set up Ribbon Button Command

Here are the property names and corresponding values:

Property

Value

Address

/WebResources/gc_AccountDetailsWijmoView.html?

PassParameters

False

Window Mode

Modal Dialog

Window Parameters

width=1400;height=800;resizable=yes

05-011-version2

Step 6: Open the Accounts Dashboard

Navigate to Accounts Home Page and click on the Analyze Accounts button. It should open the Account Dashboard as a Modal Dialog.

06-resized

Account Dashboard in Action: Group, Sort, Filter, Edit Data

Lastly, we’ll look at the interactivity that makes FlexGrid such a powerful grid tool.

Grouping: Drag any column to the section that reads “Drag columns here to create groups” and your groups will be created. We can create multiple groups as well.

Filtering: Each column has a filter icon built-in. Click the icon and we can filter based on value or conditions.

Sorting: Clicking on any of the columns sorts the data based on that column.

Data Operations: Try editing any of the cells. Once the edit is complete, click on update to update records in the CRM. The data refreshes, updating FlexGrid and pie chart automatically.

That’s all, folks! If you need more information about Wijmo 5 controls, check out our Sample Explorer and Docs. You can also download a free trial of Wijmo 5.

Project Files:

  • Source Code Project: This project contains all the source files for Account Dashboard
  • MSCRM Solution: We also created a MSCRM solution which can be directly imported into your CRM Instance and you can see the Dashboard in action at your end.

Watch the Full Video

Try Wijmo 5 now