Dynamics 365 portals: Use aggregate FetchXML queries and Chart.js

A frequently requested feature of all types of portals is to display Dynamics 365 data in different displays like charts. The Dynamics 365 portal has a pre-built chart function that utilizes the out of box CRM charting functionality so that you can take the charts you create in CRM and display then easily on the portal. This functionality is somewhat limited but using the techniques discussed in Using liquid to return JSON or XML we can easily build a services that uses FetchXML aggregate queries to build data returns to summarize or roll-up data. With the aggregated query results you can quickly work with many of the JavaScript charting libraries like d3.js, chart.js, flot.js or many others to display a rich interactive view of the data that the default chart liquid component can’t do. In this post is a simple implementation of Chart.js which is a powerful, but simple library that has a very small footprint to build some charts with the FetchXML aggregate data.

Let’s start by creating the web template that is going to return the JSON data for the chart to display. As in the previous examples we will use the fetchxml liquid tag to retrieve the data but using the aggregate function of FetchXML to summarize data. Our example is going to continue using the course schedules and instructors, for this chart we would like to get a count of schedules by instructor.

{% fetchxml feed %}
<fetch version="1.0" output-format="xml-platform" mapping="logical" aggregate="true">
  <entity name="dpx_courseschedule">
    <attribute name="dpx_coursescheduleid" alias="schedule_count" aggregate="countcolumn" />
    <attribute name="dpx_instructorid" alias="dpx_instructorid" groupby="true" />   
  </entity>
</fetch>
{% endfetchxml %}[
  {% for item in feed.results.entities %}
    {
      "count": "{{ item.schedule_count }}",
      "instructor": "{{ item.dpx_instructorid.name }}",
      "instructorid": "{{ item.dpx_instructorid.id }}"
    }{% unless forloop.last %},{% endunless %}
  {% endfor %}
]

You’ll notice this FetchXML is different from previous examples. Firstly in the opening fetch tag it removes the distinct property and adds aggregate="true". Within the entity tags the attributes are limited to one to count by with the aggregate="countcolumn" for the dpx_coursescheduleid and then because we want the count by instructor the attribute for dpx_instructorid has the property groupby="true". As in previous examples we take the results, iterate through them and create a JSON array. Also don’t forget to set the web template Mime Type to application/json and ensure that you give the return JSON template a URL using the instructions in Use liquid to return JSON or XML.

With aggregate FetchXML queries you can include filter criteria so if you want to dynamically filter the data in the aggregation you can easily add parameters that are then added as filters. You could do some cool combinations with other components that use the same technique like the previous post on FullCalendar so that as you change months or filter down your calendar data it also is filtering the chart with the same parameters.

Now that we have a JSON data return template we can build the web template that will process it with the chart.js library. Below is the basic scaffolding for our template which is a canvas HTML5 element, the chart.js Javascript library and then a document ready with the get of the canvas element.

<canvas id="myChart"></canvas>

<script src="//cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
<script>
  $(function(){  // document ready    
      var ctx = $("#myChart");
  });
</script>

Firstly within document ready we need to make a call to retrieve the data using a jQuery AJAX GET method to the JSON return template URL. With the returned JSON we will transform it into a chart.js data object so that it can be added easily to any chart type. To do so we are going to create a couple of arrays, one for labels and one for data values, then populate them by iterating through the resulting JSON array from the AJAX call.

$(function(){ // document ready
    $.ajax({
      method: "GET",
      url: "/courses-aggregate-json/"
    })
    .done(function( results ) {
      
      var labels = [], datavalues = [];
      
      for (i = 0; i < results.length; i++) { 
        labels.push(results[i].instructor);
        datavalues.push(parseFloat(results[i].count));
      }

      var dataobj = {
          labels: labels,
          datasets: [{
              data: countdata
          }]
      };

      var ctx = $("#myChart");

      // bind data to chart object
    });
});

With the returned data now formatted for chart.js data object we can initialize the chart element with a chart type and the data object. The chart.js options for the pie type are also set to animate its display and have the legend displayed at the bottom of the chart.

// inserted within the done of the ajax get
// after the transform of return JSON and creation of dataobj

var ctx = $("#myChart");

var myChart = new Chart(ctx, {
  type: 'pie',
  data: dataobj,
  options: {
    animation:{
        animateScale:true
    }, 
    legend: {
      display: true,
      position: "bottom"
    }
  }
});

After getting this web template a URL with a page template and web page you should end up with your pie chart. Although its probably looking a little gray as we didn’t assign any colors.

If you read the previous post on FullCalendar then we know that our instructors entity actually contains an attribute that holds a hex color code value which we could use to color the chart. With following code which should be located at the top of the script tag prior to the document ready method, we can get those color codes, as well as create a function that will find the color for a specific instructor.

{% fetchxml feed %}
  <fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">
    <entity name="dpx_instructor">
      <attribute name="dpx_instructorid" />
      <attribute name="dpx_colorcode" />
    </entity>
  </fetch>
{% endfetchxml %}

var instructorData = [{% for item in feed.results.entities %} {
      "instructorid": "{{ item.id }}",
      "color": "{{ item.dpx_colorcode | default:"#ffff00" }}"
    }{% unless forloop.last %},{% endunless %}
  {% endfor %}];

function findInstructorColor(lookupId) {
  for(var a = 0; a < instructorData.length; a++) {
    if(instructorData[a].instructorid == lookupId)
    {
      return instructorData[a].color;
    }
  }
}

With this code we are doing similar to the previous JSON return template but within the JavaScript so on render of the template it dynamically renders an object called instructorData which is a JSON array with the instructorid and color.

In the transform into the data object section update it with a new array called color and populate the array in the result iteration (line 6) which uses the findInstructorColor method, then include that new array in the dataobj (line 13).

var labels = [], countdata = [], color = [];

for (i = 0; i < results.length; i++) { 
  labels.push(results[i].instructor);
  countdata.push(parseFloat(results[i].count));
  color.push(findInstructorColor(results[i].instructorid));
}

var dataobj = {
  labels: labels,
  datasets: [{
    data: countdata,
    backgroundColor: color
  }]
};

With these changes you should now have a much nicer looking pie chart with color!

Using JavaScript charting libraries in combination with the FetchXML aggregate queries can allow you to start creating some really cool looking dashboards that go beyond the out of box functionality of portals. You can even use some Bootstrap components to help you do some nice formatting in combination with charts.

If your interested in learning more about liquid techniques I will be giving a webinar with xRMVirtual on April 26th at 12pm EST – Advanced Liquid Templates for Dynamics 365 portals.

1 thought on “Dynamics 365 portals: Use aggregate FetchXML queries and Chart.js”

Leave a Reply

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