Updating sample code for the Infragistics jQuery Chart

Graham Murray / Tuesday, May 8, 2012

Leading up to and after the initial CTP of igDataChart in 11.2, I posted a few blog posts on how to use the chart in some interesting real time and volume scenarios.



Due to some enhancements to the chart API and its JavaScript resource loading between then and now, we need to make a few adjustments to how those samples were set up to use them with the 12.1 RTM version of the chart.
In this post I’ll describe the necessary changes to adapt the sample code to work with the RTM bits.

Using the Infragistics Loader

The new recommended way to setup the chart is to use the Infragistics loader. The chart features have been split up into many subsets so that you don’t need to load the entire chart source if you only want to, say, load a scatter series.
The Infragistics loader will handle all the complexity of which js files and which css files you need to load based on the charting features you want to use.
So, first, we need to grab the latest files from the 12.1 install. These will be located at:


C:\Program Files (x86)\Infragistics\NetAdvantage 2012.1\jQuery


And you can go ahead and copy the js and css folders into your project.
Next we want to replace whatever lines where we were referencing any chart js/css files with a reference to the Infragistics loader. So, for example, in a razor file from one of the samples we had this:

<script src="@Url.Content("~/Scripts/IG/ig.ui.chart.min.js")" type="text/javascript"></script>
<link href="@Url.Content("~/Content/themes/base/ig.ui.chart.min.css")" rel="stylesheet" type="text/css" />

Which we should replaced with:

<script src="@Url.Content("~/js/infragistics.loader.js")" type="text/javascript"></script>

Then, in the page where we were using the chart, likely we were waiting for jQuery to be ready before trying to initialize the igDataChart like so:

$(function () {
    //chart init
});

And now, instead, we should wait for the loader to have downloaded our chart resources (and initialized jquery, etc).

$.ig.loader({
        scriptPath: "js/",
        cssPath: "css/",
        resources: "igDataChart.Category"
    });

    $.ig.loader(function () {
	//chart init
        });

The above loader invocation indicates that we will require the Category subset of functionality from the chart, and then the code placed at “//chart init” will only be run after the loader has acquired the appropriate resources.
Here’s a breakdown of the features that can be requested from the loader and what they contain:

  • Category
    • area
    • splineArea
    • bar
    • column
    • line
    • spline
    • stepLine
    • waterfall
  • RangeCategory
    • rangeArea
    • rangeColumn
  • Financial
    • financialPriceSeries
    • typicalPriceIndicator
    • absoluteVolumeOscillatorIndicator
    • averageTrueRangeIndicator
    • accumulationDistributionIndicator
    • averageDirectionalIndexIndicator
  • Polar
    • polarLine
    • polarArea
    • polarScatter
  • Radial
    • radialColumn
    • radialLine
    • radialPie
  • Scatter
    • scatter
    • scatterLine
    • bubble



Code updates


Now that we’ve updated the way that the JavaScript files are loaded, we just have to make a few adjustments to the code to account for API differences.
First, the chart now uses our own templating engine rather than jQuery templates. This, in general requires two changes:

  1. Remove any lines that look like this: $(“#tooltipTemplate”).template(“tooltipTemplate”); because our engine does not require you explicitly name them in this manner.
  2. You must remove any excess whitespace from the beginning and end of a template variable in your templates: ${ item.Value3 } -> ${item.Value3}

Secondly, there was a change to how you should notify the chart when you’ve made changes to the data sources it is bound to. In the CTP version you would notify the chart based on an name of a series or axis to which the data was assigned, e.g:

$('#chart1').igDataChart("notifyClearItems", "tweetMeter");

Now, instead of notifying based on the name of the series or axis to which the changed data source is assigned, you instead provide the actual data source that was updated:

$('#chart1').igDataChart("notifyClearItems", tweetData);

And that should be it. With these changes in place, the samples I’ve shared previously should work with the RTM version of the chart. If you run into any other issues, please let me know.

Example

As an example of updating CTP logic for use with the RTM bits, here is how we would adjust the Live Twitter Meter sample’s script reference section:

<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.6.4.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery-ui-1.8.16.min.js")" type="text/javascript"></script>
    <link href="@Url.Content("~/Content/themes/base/jquery-ui.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery.signalR.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/signalr/hubs")" type="text/javascript"></script>
    <script src="@Url.Content("~/js/infragistics.loader.js")" type="text/javascript"></script>

The main differences are the removal of the chart js and css references, and the addition of the reference to infragistics.loader.js.
And here is the updated logic for Index.cshtml:

@{
    ViewBag.Title = "Home Page";
}
<style>
    #chart1
    {
        float: left;
    }
    #chart2
    {
        float: left;
        padding-left: 10px;
    }
    #legend
    {
        float: left;
        padding-left: 10px;
    }
</style>
<style>
    .ui-chart-palette-1
    {
        border-color: rgb(35, 128, 168);
        border-color: rgba(35, 128, 168);
        background-color: rgb(68, 172, 214);
        background-color: rgba(68, 172, 214);
    }
    
    .ui-chart-palette-2
    {
        border-color: rgb(51, 51, 51);
        border-color: rgba(51, 51, 51);
        background-color: rgb(73, 73, 73);
        background-color: rgba(73, 73, 73);
    }
    
    .ui-chart-palette-3
    {
        border-color: rgb(128, 128, 128);
        border-color: rgba(128, 128, 128);
        background-color: rgb(168, 168, 168);
        background-color: rgba(168, 168, 168);
    }
    
    .ui-chart-palette-4
    {
        border-color: rgb(24, 81, 112);
        border-color: rgba(24, 81, 112);
        background-color: rgb(33, 110, 153);
        background-color: rgba(33, 110, 153);
    }
    
    .ui-chart-palette-5
    {
        border-color: rgb(135, 153, 34);
        border-color: rgba(135, 153, 34);
        background-color: rgb(164, 186, 41);
        background-color: rgba(164, 186, 41);
    }
    
    .ui-chart-axis
    {
        border-color: #989EA3;
        background-color: #989EA3;
    }
</style>
<script>
    $.ig.loader({
        scriptPath: "js/",
        cssPath: "css/",
        resources: "igDataChart.Category"
    });

    $.ig.loader(function () {
        var tweetMeterHub = $.connection.tweetMeterHub, joined = false, tweetData = [], currMax = 100, maxOutOfDate = 0, tweetHistoryData = [], currDateString, newHistoryItem, oldHistoryItem;

        tweetMeterHub.rateUpdate = function (rateMessage) {
            var items = rateMessage.Rates, output = "", first = true, maxRate = 0, i, key, currDate = new Date(),
            hours = currDate.getHours(), minutes = currDate.getMinutes(),
            seconds = currDate.getSeconds();

            $.each(items, function (i, item) {
                if (first) {
                    first = false;
                } else {
                    output += ", ";
                }
                output += item.Keyword + " = " + item.Rate.toString();
            });

            for (i = 0; i < tweetData.length; i++) {
                key = tweetData[i].label;
                $.each(items, function (i, item) {
                    if (item.Keyword == key.toLowerCase()) {
                        maxRate = Math.max(maxRate, item.Rate);
                        tweetData[i].rate = item.Rate;
                    }
                });
            }

            if (maxRate > currMax) {
                currMax += 100;
                $('#chart1').igDataChart("option", "axes", [{ name: "xAxis", maximumValue: currMax}]);
                $('#chart2').igDataChart("option", "axes", [{ name: "yAxis", maximumValue: currMax}]);
            } else if (currMax - maxRate > 100) {
                maxOutOfDate++;
                if (maxOutOfDate > 5) {
                    maxOutOfDate = 0;
                    currMax -= 100;
                    $('#chart1').igDataChart("option", "axes", [{ name: "xAxis", maximumValue: currMax}]);
                    $('#chart2').igDataChart("option", "axes", [{ name: "yAxis", maximumValue: currMax}]);
                }
            }

            $('#chart1').igDataChart("notifyClearItems", tweetData);
            $('#infoBoxContent').prepend('<li>' + output + '</li>');

            if (hours > 12) {
                hours = hours - 12;
            }
            if (hours < 10) {
                hours = "0" + hours;
            }
            if (minutes < 10) {
                minutes = "0" + minutes;
            }
            if (seconds < 10) {
                seconds = "0" + seconds;
            }
            currDateString = hours + ":" + minutes + ":" + seconds;

            newHistoryItem = {};
            newHistoryItem.label = currDateString;
            $.each(items, function (i, item) {
                newHistoryItem[item.Keyword.toLowerCase()] = item.Rate;
            });
            tweetHistoryData.push(newHistoryItem);
            $("#chart2").igDataChart("notifyInsertItem", tweetHistoryData, tweetHistoryData.length - 1, newHistoryItem);
            if (tweetHistoryData.length > 2000) {
                oldHistoryItem = tweetHistoryData.shift();
                $("#chart2").igDataChart("notifyRemoveItem", tweetHistoryData, 0, oldHistoryItem);
            }
        };

        $("#addKeyword").click(function () {
            tweetData.push({ label: $('#keyword').val(), rate: 0 });
            $("#chart1").igDataChart("notifyInsertItem", tweetData, tweetData.length - 1, tweetData[tweetData.length - 1]);

            $("#chart2").igDataChart("option", "series", [{
                name: $("#keyword").val().toLowerCase(),
                title: $("#keyword").val(),
                type: "line",
                xAxis: "xAxis",
                yAxis: "yAxis",
                trendLineType: "linearFit",
                trendLineBrush: "rgba(117,5,33,.6)",
                thickness: 3,
                trendLineThickness: 2,
                valueMemberPath: $("#keyword").val().toLowerCase()
            }]);

            if (!joined) {
                joined = true;
                tweetMeterHub.join();
            }
            tweetMeterHub.addKeyword($('#keyword').val());
        });

        $("#removeKeyword").click(function () {
            var removed = null, i = 0;
            for (i = 0; i < tweetData.length; i++) {
                if (tweetData[i].label == $("#keyword").val()) {
                    removed = tweetData[i];
                    tweetData.splice(i, 1);
                    break;
                }
            }
            if (removed) {
                $("#chart1").igDataChart("notifyRemoveItem", tweetData, i, removed);

                $("#chart2").igDataChart("option", "series", [{
                    name: $("#keyword").val().toLowerCase(),
                    remove: true
                }]);
            }

            if (!joined) {
                joined = true;
                tweetMeterHub.join();
            }
            tweetMeterHub.removeKeyword($('#keyword').val());
        });

        $("#chart1").igDataChart({
            width: "400px",
            height: "500px",
            dataSource: tweetData,
            axes: [{
                name: "yAxis",
                type: "categoryY",
                label: "label"
            }, {
                name: "xAxis",
                type: "numericX",
                minimumValue: 0,
                maximumValue: 100,
                labelExtent: 50
            }],
            series: [{
                name: "tweetMeter",
                type: "bar",
                xAxis: "xAxis",
                yAxis: "yAxis",
                title: "Tweet Meter",
                valueMemberPath: "rate",
                transitionDuration: 1800
            }]
        });

        $("#chart2").igDataChart({
            width: "500px",
            height: "500px",
            dataSource: tweetHistoryData,
            legend: { element: "legend" },
            axes: [{
                name: "xAxis",
                type: "categoryX",
                label: "label",
                labelExtent: 50
            }, {
                name: "yAxis",
                type: "numericY",
                minimumValue: 0,
                maximumValue: 100
            }]
        });

        $.connection.hub.start();
    });
</script>
<div id="chart1">
</div>
<div id="chart2">
</div>
<div id="legend">
</div>
<div id="infoBoxContainer" class="ui-widget-content ui-corner-all" style="width: 500px;
    height: 200px; overflow: auto">
    <ul id="infoBoxContent">
    </ul>
</div>
<input type="text" id="keyword" />
<input type="button" id="addKeyword" value="Add Keyword" />
<input type="button" id="removeKeyword" value="Remove Keyword" />

The rest of the code from that sample, unless you are using a different version of SignalR, should remain the same. If using a newer version of SignalR, you may need to replace references to ClientId with ConnectionId.
Here’s the video result from last time:



Happy charting!
-Graham