Tag

chartjs

Browsing

There’s may chart out there, free and very expensive ones. You would have expected slightly cheaper prices on the commercials ones, seeing that representing data visually in a chart, is as old as the list box. Lucky, there is Chart.js, which is open source.

It’s a javascript library, so using it with ASP.NET webforms, need some tricks. Making jQuery calls via AJAX is our medicine.

Something else I found on the internet, there is everyone doing it differently and nobody has a complete solution. To be dynamic, it is not just the data that should be dynamics, but also things like chart type, chart options, width, height, container titles and footers, etc. I worked through the night and quickly put together my take on it. Yea, lots of improvements needed as I just slapped things together to get it to work. Cleanup can come later.

The website I’m working on is using bootstrap, charts should be responsive, and it’s tiled together with other elements using Masonry.

Clientside

This is my compact client-side script:

var uCharts = [56];
        var i; for (i = 0; i < uCharts.length; i++) { CreateCharts(uCharts[i]); TileGrid(); }

        function CreateCharts(id) {
            try {
                var ctx = $('#ucc' + id)[0].getContext("2d");
                return $.ajax({
                    url: "/Services/ChartData.asmx/Load", data: '{UserChartId: ' + id + '}',
                    type: "POST", contentType: "application/json; charset=utf-8", dataType: "json",
                    success: function (res, textStatus) {
                        var d = JSON.parse(res.d[0]); var o = JSON.parse(res.d[1]); var c = res.d[6];
                        var t = res.d[2]; var f = res.d[3]; var w = res.d[4]; var h = res.d[5];
                        $('#ucc' + id).attr('width', w).attr('height', h);
                        var myChart = new Chart(ctx, { type: c, data: d, options: o });
                        $('#uct' + id).text(t); $('#ucf' + id).text(f);
                        TileGrid();
                    },
                    error: function (res, textStatus) { }
                });
            }
            catch (err) { }
            finally { }
        }

        function TileGrid() {
            var $grid = $('.grid').masonry({
                itemSelector: '.grid-item',
                columnWidth: '.grid-sizer',
                percentPosition: true,
                horizontalOrder: false,
            });
        }
  1. First I have an array of ids (uCharts). These are the ids of the charts a logged-in user want to display on his dashboard.
  2. I loop through these ids and call the cart creator function CreatCharts for each of the charts. These charts are inside a masonry titled grid, so I call TileGrid each time after a tile was created.
  3. The jQuery ajax call get back and array. Usually “d” only contains the JSON data, but I have added options, title, footer, width, height and chart type to the string being return so that everthing about the chart is done server-side.
  4. When I call the web service, I also pass the users’ chart id, so that the web service can do its thing server side.

Server Side

The service receives the identifier (user’s chart id in my case), and use this id to get information about this chart from wherever needed. In the code below, I have just hard coded some dummy data, but in real life, I will go and fetch the data and chart options from the database.

using System.Web.Script.Serialization;
using System.Web.Services;

namespace WLMT.Services
{
    [System.Web.Script.Services.ScriptService]
    public class ChartData : System.Web.Services.WebService
    {
        [WebMethod(EnableSession = true)]
        public string[] Load(int UserChartId)
        {
            var data = new
            {
                labels = new[] {
                    "South Western", "Western", "Northern", "North Eastern", "Eastern"
                },
                datasets = new[] {
                    new {
                        label = "2016 - 2017",
                        backgroundColor = new[] { "rgba(220, 220, 220, 0.5)" },
                        pointBorderColor = "#fff",
                        data = new[] {"312","260","350","275","230"}
                    },
                    new {
                        label = "2017 - 2018",
                        backgroundColor = new[] { "#3c8dbc", "#00c0ef", "#00a65a", "#f39c12", "#605ca8" },
                        pointBorderColor = "#fff",
                        data = new[] {"350","280","375","300","260"}
                    }
                }
            };

            var options = new
            {
                responsive = true,
                maintainAspectRatio = true,
                scales = new
                {
                    xAxes = new[]
                    {
                       new {
                           gridLines = new { display = true }
                       }
                    },
                    yAxes = new[]
                    {
                        new {
                            gridLines = new { display = true },
                            scaleLabel = new { display = true, labelString = "Millions" }
                        }
                    }
                }
            };
            string ChartTitle = "ChartJS Responsive & Aspect Ratio Test";
            string ChartFooter = "Data is fetch with a web service from client side";
            string Width = "400";
            string Height = "250";
            string ChartType = "bar";

            string[] result = { data.ToJSON(), options.ToJSON(), ChartTitle, ChartFooter, Width, Height, ChartType };
            return result;
        }
    }
+}

Array[0] and Array[1] values is converted to JSON. The other array values just plain strings. If you go back to the client-side script, you will see that I read all array values from the response (res.d[0] – res.d[6]) and use it where applicable.

My JSONHelper is in another class, see below.

Helper

using System.Web.Script.Serialization;

namespace WLMT
{
    public static class JSONHelper
    {
        public static string ToJSON(this object obj)
        {
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            return serializer.Serialize(obj);
        }

        public static string ToJSON(this object obj, int recursionDepth)
        {
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            serializer.RecursionLimit = recursionDepth;
            return serializer.Serialize(obj);
        }
    }
}

The end result is shown below.

The width and height passed to the chart are only for maintaining the aspect ratio, as Chart.js has “responsive” = true and “maintainAspectRatio” = true set.

In my HTML, I have a bootstrap card, where card title is used for the title, body for the chart canvas etc. These placeholder containers are created by the page, server-side, and so is the javascript array I place on the page. So each placeholder card will be in the HTML when in reach the client browser, from where the client-side script will do its thing to populate the placeholder with its chart.

Stuff can go wrong, as charts are created by users using a web-based wizard. For now, the client-side code is in a try-catch block, so that all client-side stuff does not fall over when something goes wrong here. In the catch, I will be removing the empty bootstrap placeholder card. The issue will be logged to ELMAH, from where action can be taken. A notification will tell the user that the card is not shown due to…