ShowTable of Contents
This article intends to provide developers with an understanding of how to integrate the dojox charting classes into XPages to generate a functionally rich chart in a variety of formats. By stepping through the code and providing a sample database, I will show you how to add colour themes, customise axes, and generate tooltips and legends. The intention is that you are able to build customer-ready charts quickly with a level of knowledge to be able to dig deeper if required. You can see the charts online here:
http://hermes.intec.co.uk/xpagessamples/charts.nsf/chart1.xsp
Generic Code for Dojox Charting
The first step is to include all the dojo setup. From the All Properties panel of the XPages properties, set dojoParseOnLoad and dojoTheme to true. Without dojoTheme the tooltips do not show. We also need to include all the dojoModules being used. If you have not done this before, again this in done from the All Properties panel, from the Resources property in the Basics group. The main dojo module that covers the charts is dojox.widget.charting.Chart2D (dojo modules are javascript files, so the names are case sensitive).
One of the useful things about dojox charting are the wide range of themes that are available out-of-the-box to include as modules. The full list can be found and experimented with in your \Data\domino\js\dojo-1.3.2\dojox\charting\themes directory. As with XPages OneUIv2 themes, this gives the ability to allow users to choose their own theme or to choose a standard theme for different customers or applications. And if you can't find a theme that fits in with your application, for the top-level themes it should be pretty easy to add your own theme, just an array of colours.
(function(){
var dxc=dojox.charting;
dxc.themes.MiamiNice=new dxc.Theme({
colors: [
"#7f9599",
"#45b8cc",
"#8ecfb0",
"#f8acac",
"#cc4482"
]
});
})();
}
The final dojo-related generic setting is code on the beforeRenderResponse that forces compatibility mode for Internet Explorer 8. For charts, Internet Explorer 8 throws an error of "this.rawNode.path is null or not an object" on line 20 in dojo.js and the charts don't load. (Line 20 is the whole dojo.js, because it is compressed into one line). Forcing compatiability mode avoids the error and loads the charts.
try {
if (context.getUserAgent().isIE(8, 8)) {
var exCon = facesContext.getExternalContext();
var response = exCon.getResponse();
response.setHeader("X-UA-Compatible", "IE=EmulateIE7");
}
} catch (e) {
}
Pie Charts
Creating a Series
The first part of the chart is the series. What I'm looking to produce is an array of JSON objects as a variable, one per portion of the pie, holding the value along with all the other parameters I want to use for my pie. If you view the source of Chart 1, you will see the full variable. The output is in this format:
var series1=[{y: 3, text: "ABC DEVELOPMENT",legend: "ABC DEVELOPMENT", tooltip: "ABC DEVELOPMENT (3)"},{y: 5, text: "ABC SUPPORT",legend: "ABC SUPPORT", tooltip: "ABC SUPPORT (5)"}];
I'm producing my output in two steps. The first step is a Computed Field control called seriesA to create a list of JSON objects in a string. The second step is a Computed Field control that creates client-side javascript on the XPage to add the "var series=1[" and "]". This makes it easier to read for those trying to follow the tutorial, but in practice I think it would also make it easier for a developer supporting the XPage.
The code for Computed Field seriesA is:
var viewNav:NotesViewNavigator = view1.createViewNav();
var viewEnt:NotesViewEntry = viewNav.getFirst();
var output:string = "";
while (viewEnt != null) {
if (viewEnt.getColumnValues()[5] > 0) {
/* parameters:
@y: number, corresponding to size of pie
@text: label (can be suppressed by setting 'labels' parameter of pie is set to false)
@color: colour of the pie section. Can be a colour name (e.g. "blue"), rgb or hex colours.
If dojox.charting.action2d.Highlight is used, the highlight colour is the negative (so if color is black, highlight is white)
@legend: can be used to override the text parameter for legends
@tooltip: if dojox.charting.action2d.Tooltip is used, this is the tooltip that's added.
*/
output += "{y: " + viewEnt.getColumnValues()[5] + ", text: \"" + viewEnt.getColumnValues()[1] + "\",legend: \"" + viewEnt.getColumnValues()[1] + "\", tooltip: \"" + viewEnt.getColumnValues()[1] + " (" + viewEnt.getColumnValues()[5] + ")\"},";
}
viewEnt = viewNav.getNext(viewEnt);
}
return output.substr(0, output.length - 1)
This creates a NotesViewNavigator and uses a while loop to pull content from the view entries to create a comma-delimited list of JSON objects, each corresponding to one portion of the pie. So the first one will be:
{y: 3, text: "ABC DEVELOPMENT",legend: "ABC DEVELOPMENT", tooltip: "ABC DEVELOPMENT (3)"},
Within the while loop there is an if statement ensuring I'm only creating a JSON object if the value I'm pulling in - viewEnt.getColumnValues()[5] - is greater than zero. If the value is zero, then it would not show in the pie but would show in the legend, which I don't want.
Because the output adds a comma after each JSON object, the final line removes that final comma from the end of the string.
It doesn't matter whether you loop through NotesViewEntries or use NotesViewDocuments or use an @DbLookup or @DbColumn to pull in JSON objects from a column. All these would allow you to get a handle on the documents, and so compose the JSON output needed.
Each JSON object has a number of parameters, as shown in the screen shot. Using an HTML table store only allows the "y" parameter to be set, which is rather limiting. Using JSON objects offers much greater flexibility. A custom label can be added, which is shown against that portion of the pie. The label parameter is included here only for information of what can be done. In my sample pie chart I suppress it by setting the "labels" parameter of the pie chart itself to false. I have done that because I could not generate short labels and the pie has a lot of portions. If you change the labels parameter on the pie chart to true in the sample, you will see what I mean. The colour can be defined - by default the colours are handled by the theme and it could be tricky to define colours in the series unless you know the number of objects to be put into the series. As with any colours, this can accept a colour name, a hex value or an rgb value. A legend label for this portion of the pie can be defined. If this parameter is omitted, the text parameter is used instead on the legend. And a tooltip can be defined. This will be shown when you hover on that portion of the pie. In this example of a pie chart of open calls, I'm also including the number of open calls in the tooltip.
So this creates the text list of JSON objects which will be put into series1. For step 2 I use a Computed Field control that writes client-side javascript onto the XPage. If you've never done this before, it's the same as putting pass-thru HTML on a web form.
<!-- Now pull the series into client-side javascript variables -->
<xp:text escape="false" id="computedField1">
<xp:this.value><![CDATA[#{javascript:
var result = "<script language=\"JavaScript\" type=\"text/javascript\">";
result += "var series1 = [";
result += getComponent("seriesA").getValue() + "];";
result += "var series2 = [";
result += getComponent("seriesB").getValue() + "];";
result += "var series2Tooltip = [";
result += getComponent("seriesBTooltip").getValue() + "];";
result += "</script>";
return result;
}]]></xp:this.value>
</xp:text>
The important step is that Content Type is set to HTML (this is the escape="false"). The output of the Computed Field seriesA just had the JSON objects as a string, so the javascript needs to wrap it in "[]" to convert the string to an array of objects. And the whole thing is stored in javascript variable series1. I'm also adding javascript variables for two other series that I'll be using in the column and bar charts. I will cover those in the next part of the tutorial.
You will notice this doesn't use repeat controls. In theory, it should be possible to do that, wrapping the repeat within Computed Text controls opening and closing the client-side javascript. If people feel that would be useful, I will add a version of the pie chart using a repeat control for the series.
Generating the Pie Chart
Chris Connor has shown how to create a pie chart via HTML markup. In this example I will be creating the pie chart via javascript and adding it via the XSP.addOnLoad function. The first part of generating the pie chart is to add onto the XPage two panels in which the pie chart and its legend will be inserted via javascript.
<!-- These are the panels to hold the charts -->
<xp:panel id="simplechart"
style="width: 450px; height: 450px;">
</xp:panel>
<xp:panel id="Legend"></xp:panel>
The rest of the code is put in an Output Script control (xp:ScriptBlock). This comprises a variable called makeCharts that contains a function. The final line of the code calls "XSP.addOnLoad(makeCharts)". If you've not used this before, the addOnLoad function is in the XSPClientDojo.js file which can be found in your \Data\domino\js\dojo-1.3.2\ibm\xsp\widget\layout folder. As its name suggests this is an XPages function that runs javascript code on load of the XPage, so will run the makeCharts function.
<xp:this.value><![CDATA[makeCharts = function(){
// Code to create the pie chart
var chart1 = new dojox.charting.Chart2D("#{id:simplechart}");
chart1.setTheme(dojox.charting.themes.PlotKit.blue);
/*
Pie Chart @params
@labelStyle, @ticks, @precision, @fixed appear to have no effect
@labelOffset - the larger the positive number, the nearer the centre; the larger the negative number, the further from the centre
@radius controls the size of the pie chart
@fontColor can be colour name, rgb or hex
@font sets font elements, e.g. font: "normal normal bold 14pt Tahoma"
*/
chart1.addPlot("default", {type: "Pie", radius: 150, fontColor: "rgb(255,0,0)", labels: false});
chart1.addSeries("Series 1", series1);
The first part of the makeCharts function (shown above) creates a variable for the chart. The "#{id:simplechart}" runs server-side javascript to calculate the full id of the panel where we want to place the chart. I could have used a div (not xp:div) with a simple id. But using an xp:panel with an id of "simplechart" and "#{id:simplechart}" means that if the code is used in a custom control repeated multiple times on an XPage, we never get a conflict for the id. The next line sets the theme for the chart. Then I call the addPlot function which defines the chart type, in this case "Pie", and all parameters. The first parameter is the name, usually "default" unless you have more than one plot type on the chart. (I haven't experimented with this, so I'm not sure how to add a series to a particular plot type, but you will notice that with the animations we also define the name the animation relates to.)
As you can see in the screenshot above, I've included the parameters available for the pie chart. You will see I have set the labels parameter to false. This means that even though I have defined the text parameter in my series, it is ignored and no labels are produced. As I mentioned above, this can be useful if your labels are quite lengthy or you have a lot of portions on your pie. Font and fontColor then relate to the font settings of the labels. For those who have not used it before, fontColor gives an example of how to set an rgb value for a colour in HTML. The final numbered line adds the series. Obviously for the pie chart there is only one series defined.
//ANIMATIONS - use Magnify OR MoveSlice OR Shake
/* Magnify @params
@duration: Duration in ms
@scale: amount to expand the slice by
@shift: amount to shift from centre
*/
var anim_a = new dojox.charting.action2d.Magnify(chart1, "default", {scale: 1.2, shift: 10});
// MoveSlice has additional param @easing: various animation events, by default dojo.fx.easing.backOut.
// See dojo.fx.easing for alternatives
//var anim_b = new dojox.charting.action2d.MoveSlice(chart1, "default", {scale: 1.2, shift: 10});
/* Shake @params
@duration: Duration in ms
@easing: various animation events, by default dojo.fx.easing.backOut.
@shiftX: amount to shake in x axis
@shiftY: amount to shake in y axis
var anim_c = new dojox.charting.action2d.Shake(chart1, "default", {shiftX: 10, shiftY: 10});
*/
/* Highlight @params
@highlight: the colour of the highlight (by default negative of the colour). Can be name, rgb or hex
@duration: Duration in ms
*/
var anim_d = new dojox.charting.action2d.Highlight(chart1, "default", {highlight: "#000000"});
// Tooltip @param @text expects a function. This gets @tooltip or @text @param from series (in that order)
var anim_e = new dojox.charting.action2d.Tooltip(chart1, "default");
After defining the chart type and adding the series, I then add the animations. You should only use one of Magnify, MoveSlice and Shake because the animation is very similar: they all animate the slice selected. Magnify and MoveSlice are very similar, both have parameters for duration, the amount to expand the slice by ("scale") and the amount to move the slice from the centre ("shift"). MoveSlice has an additional parameter to define the style for animation, by default dojo.fx.easing.backOut. For alternatives see dojo.fx.easing. (The javascript files installed as part of the Notes Client and Domino installs are compressed, but if you want a more readable version of dojo files I would recommend Dojomino - the new Filter option in Designer 8.5 makes this even more usable - or downloading the files from the dojo site.) Shake allows you define the animation style with the easing parameter, but also allows you to define the extent to which the pie portion is shaken in axes x and y.
The next animation is the Highlight animation. Again this has a duration parameter but also a highlight colour. If omitted the default is the negative of the current colour, but you can specify a specific highlight colour, as I have.
The final animation is to generate the tooltip. You need to ensure dojoTheme is set to True on the XPage or custom control for tooltips to show. The tooltip displays the tooltip parameter of the series, if available, otherwise the text parameter. You can override the text parameter, and I will show that in action in the next part when I show the bar and column charts, because those charts only accept a series of numbers and not a series of objects.
chart1.render();
var legendTwo = new dojox.charting.widget.Legend({
chart: chart1, horizontal: false
},
"#{id:Legend}");
The only thing remaining to create the chart is the call the render function.
As with the chart, the next step is to add the legend. Again this is defined as a variable containing the parameters and the panel id where we want to place the legend. The legend has two parameters, the first of which defines the chart the legend applies to. For a pie chart this creates a legend entry for each JSON object in the series, using for each object in the series either the legend parameter, if it exists, or the text parameter. The second parameter defines the display format, horizontal as true or false (the default is true if the parameter is omitted). If you look at the dojox\charting\widget\Legend.js javascript file you will see the legend is created as an HTML table. As a result the default option of horizontal may work well if you have only a small number of elements in your legend, otherwise you will want to set horizontal to false, as I have. The challenge with creating your own legend outside of dojo is getting the icon that the legend title relates to from the _makeIcon function. For a pie chart, if you're specifying the colours, this could be done more easily. Otherwise it would be easier to create your own extension of dojox\charting\widget\Legend.js (e.g. amending the refresh function to start a new row every n entries). This could easily be manually copied onto a Domino server, the challenge would be how to deploy it from an nsf via an agent.
Column and Bar Charts
Adding the Series
If you try to apply the series we generated for the pie chart to a column or bar chart, you will find you get no data plotted. This is because of a limitation on the column and bar charts, that they will not accept an array of objects, just a one-dimensional array of values. So you can show your end user, e.g., a bar chart with bars and on your y axis show the numbers. But what does each bar denote? If your y axis has tick steps (the difference between one label and another on the axis) for 100s or 1000s, how do you tell the user the actual value of each bar? If you add a legend to a bar or column chart, as we did for the pie chart, the legend only labels the overall series, not the individual elements of the series. You could add a table as well, but you wouldn't want to show both on a dashboard of various charts.
If you've followed the link and then continued onto the dojo forum, there is a workaround. Even with the answer it took me a little while to understand how to implement it and I couldn't find an example. So hopefully this will help anyone else get a head-start in using bar or column charts.
So first I add a Computed Field control to hold the values, calling it SeriesB. I've used the same approach of looping through view entries, writing a comma-delimited list of numbers, stripping off the final comma:
<!-- Series used for column / bar chart. Note: this type of chart can only accept an array of numbers. Labels etc are ignored -->
<xp:text id="seriesB" rendered="false">
<xp:this.value><![CDATA[#{javascript:var viewNav:NotesViewNavigator = view1.createViewNav();
var viewEnt:NotesViewEntry = viewNav.getFirst();
var output:string = "";
while (viewEnt != null) {
if (viewEnt.getColumnValues()[5] > 0) {
output += viewEnt.getColumnValues()[5] + ",";
}
viewEnt = viewNav.getNext(viewEnt);
}
return output.substr(0, output.length - 1)}]]></xp:this.value>
</xp:text>
I then add a second Computed Field control to hold the tooltip I want to attach to each bar, and I call that SeriesBTooltip. The code is identical except for the "output += ..." line - I'm getting the Contract Name and appending the value in brackets, so I get something like this:
"ABC DEVELOPMENT (3)","ABC SUPPORT (5)","ACME EDI/AS2 DEVELOPMENT (1)","ACME SALES DOCUMENT REPOSITORY (6)",
I then just need to push these two series into client-side javascript variables, using an additional Computed Field control with the cape property set to "false" so it is rendered as HTML rather than plain text. This was the Computed Field control from Part One, shown below with the two new series highlighted.
There is no reason why this could not be done with an HTML data table, as shown in Chris Connor's sample charts, or any other method using a repeat control.
Creating the Column Chart
The code to create the bar and column charts is virtually the same - one displays the values horizontally, the other displays them vertically. This shows the same data I displayed in the pie chart.
var chart2 = new dojox.charting.Chart2D("#{id:simplechart2}");chart2.setTheme(dojox.charting.themes.MiamiNice);
chart2.addPlot("default", {type: "Columns", gap: 1});
/* addAxis @params
@includeZero forces it to start at zero and adds a zero label at the beginning
@minorTicks, @microTicks adds labels for those elements
@majorTick, @minorTick, @microTick sets styles for those ticks. @Params are @length, @stroke/@color (stroke colour - name, rgb, or hex)
@majorTickStep, @minorTickStep, @microTickStep forces the steps between those ticks on the axis
@stroke sets the colour for the axis line
@font, @fontColor set the font characteristics
@min, @max set limits of the axis
*/
chart2.addAxis("y", {vertical: true, min: 0, max: 20});
// columns and bars chart can only accept a series of numbers, not objects
chart2.addSeries("Series 2", series2);
// Shake, Magnify and MoveSlice don't seem to work
var anim_d = new dojox.charting.action2d.Highlight(chart2, "default", {highlight: "#000000"});
First I create the chart variable, set the theme, and add the plot with type as "Columns" or "Bars". An additional parameter is available for these two chart types, called gap, to define the gap between each bar / column. Then I add an axis for whichever axis is measuring the values (y axis for the column chart, x axis for the bar chart). The other axis will only have bars on it.
The axis has two parameters, a name and an array of options. The name corresponds to the name of an axis defined on a plot. The default axis names are x (horizontal) and y (vertical). The optional parameters are listed in the screenshot. Min and max allow you to fix the upper and lower limits of the axis, as I have done in the column chart. However, it seems that a line is still shown where the end of the column would have finished. Instead of "min: 0" I could have used "includeZero: true". For the parameters affecting ticks, see the bar chart.
Then I add the series of numbers - series2. Finally I add the animation. For column and bar charts the shake, magnify and moveSlice animations don't seem to work, so all I'm doing is highlighting the default plot on chart2, changing the bar colour to black.
So far the chart will look nice, but will not tell the user what information is being presented. I could add a legend, but as you will see from the bar chart, this is not particularly informative. I could add custom axis labels to the x axis, but the labels are horizontal, so they would not show correctly. Tooltips seem to be the best option, but the default tooltip will just show what is in series2, the number of open calls. Fortunately Eugene Lazutkin posted an answer on the dojotoolkit forum, code to override the text function of the dojox.charting.action2d.Tooltip.
//So for Tooltip animation, we need to override the text with a new functionvar myTooltip = new dojox.charting.action2d.Tooltip(chart2, "default", {
text: function(o){
// o can contain:
// - event --- a raw event object
// - type --- can be "onmouseover", "onmouseout", "onclick" or "onplotreset"
// - run --- a Series object
// - plot --- a Plot2D object
// - index --- the numeric index into series
// - element --- can be "bar", "column", "circle", "slice", "marker"
// - shape --- a dojox.gfx shape object for the element
// - hAxis --- a horizontal axis object or null
// - vAxis --- a vertical axis object or null
// - x --- an x value (on the horizontal axis)
// - y --- a y value (on the vertical axis)
// - cx, cy --- a center of the marker/circle/slice in geometric coordinates
// - cr --- a radius of circle/slice in geometric coordinates
//text @param is a function, o is the series passed into the chart, so series2Tooltip[o.index] is the nth element of series2Tooltip array
return series2Tooltip[o.index];
}
});chart2.render();
As with the normal tooltip code and any other animation, the first two parameters are the javascript variable I've used for the chart (chart2) and the plot I'm applying this tooltip to ("default"). Unlike the normal tooltip code I used for the pie chart, but like the other animations, I then add an object containing further properties. Here I override the text parameter with a different function (text: funcon(o) {...}). o is the series that the plot uses, so series2. You can see from the code above the list of parameters o can contain, the important one being o.index. This returns 0 for the first bar, 1 for the second bar etc. So as the code attaches a tooltip, it uses that bar's index number to get the relevant value from the series2Tooltip javascript array we created earlier. The outcome is that the tooltip, instead of just showing the number of open calls also shows the contract name, helping to clarify the chart for the user.
Creating the Bar Chart
The bar chart is very similar, so I've used a few additional settings. The ticks parameters allow you to define parameters for the markers on the axis, as I have for the bar chart example:
var chart3 = new dojox.charting.Chart2D("#{id:simplechart3}");chart3.setTheme(dojox.charting.themes.PlotKit.green);
chart3.addPlot("default", {type: "Bars"});
chart3.addAxis("x", {includeZero: true, minorTicks: true, minorTickStep: 5, microTickStep: 1, majorTick: {color: "blue", stroke: "red", length: 3}});
/* addPlot("Grid"... @params
@type: "Grid"
@hAxis, @vAxis: the horizontal and vertical axes this grid applies to, by default x and y, but can be attached to a different set of axes
So here I'm showing minorTicks, forcing the minorTickStep to be evey 5 and the microTickStep to be every 1. I found that if I set the maximum to 20, bars (or columns) of 1 open call were shown. However, when I allowed the axis to default to the largest bar, bars of 1 open call didn't show. By forcing the microTickStep to 1, this ensures they do appear. For the majorTick I'm adding an additional object to set the colour of the font, the colour of the stroke and the length. As with all the properties, defaults are used if you dso not explicitly define them, and can be found in the javascript file. By now I'm sure you've worked out where to find the files, but if not, it's \Data\domino\js\dojo-1.3.2\dojox\charting\Chart2D.js.
Line Chart via Javascript
In this part I will show how to generate the first of two line charts. This will use javascript functions, in the same way we have so far, adding axes with custom labels, a grid and a legend. In the next part I will show a similar line chart using an HTML data store and rendered using HTML markup.
Creating the Series
What I'm creating here is a line chart with one line per Client, showing the totals of various types of calls. I will pass over the standard code added to the XPage (adding the dominoView data element, the dojoModules, and the beforePageLoad code to force compatibility mode in Internet Explorer). I will move directly to the code used to create the javascript series. Previously I've used one Computed Field control to generate the series and a second Computed Field control to generate the javascript code. Now that I have demonstrated a few examples of that, I'm making it a bit more complex, and combining both steps into a single Computed Field. Furthermore, I'm creating multiple series, one for each line on the chart, and I shall put all those series into one javascript variable. Then I will create a separate array for the values I want to show in the legend.
<!-- What we need here is an array of series, one for each customer, with totals for each call type, e.g. [1,3,7,2,0,0]
Considered using existing view to get customer names using @Unique(@DbColumn(@DbName(),"liveCallsByClientContract",1)) then
(1) using a for loop with @Sum(@DbLookup(... for each column or
(2) using getAllEntriesByKey and looping through the columnValues for each
This option seemed best for scalability of performance
-->
<xp:text id="series" escape="false">
<xp:this.value><![CDATA[#{javascript:var result = "<script language=\"JavaScript\" type=\"text/javascript\">";
result += "var series=new Array();"
var nav:NotesViewNavigator = view1.createViewNav();
var ent:NotesViewEntry = nav.getFirst(); // This is a category level
var i=0;
var test:double = 0;
var title = "seriesT=[" //Titles series, this will be used for the final series name, to ensure legend is useful
while (ent != null) {
test = ent.getColumnValues()[3] + ent.getColumnValues()[4] + ent.getColumnValues()[5] + ent.getColumnValues()[6] + ent.getColumnValues()[7] + ent.getColumnValues()[8]
//We only want to add series if there is anything to report and don't want the final total row
if (test > 0 && !ent.isTotal()) {
result += "series[" + i + "]=[" + ent.getColumnValues()[3] + "," + ent.getColumnValues()[4] + "," + ent.getColumnValues()[5] + "," + ent.getColumnValues()[6] + "," + ent.getColumnValues()[7] + "," + ent.getColumnValues()[8] + "];";
title += "\"" + ent.getColumnValues()[0] + "\",";
i++;
}
ent = nav.getNextSibling()
}
result += title.substr(0, title.length - 1) + "]";
result += "</script>";
return result;}]]></xp:this.value>
</xp:text>
I create the Computed Field control, again setting escape="false" to make the output HTML. The value is computed javascript, in which I first create a "result" variable where we will assemble the output. I initialise this variable with a script tag and a variable array called "series". In the previous charts I created a string object because it was containing a single series. But this time because I'm creating an array of series, one for each line of the report, I have chosen to create an array and add series to elements in the array. Each line will be for a client and contain numbers for New, Closed, Open, UAT, Maintenance and Long calls. So the "series" array will look like this:
series[0]=[0,0,8,0,0,8];series[1]=[0,0,28,18,0,23];series[2]=[0,0,1,0,0,0];series[3]=[1,0,5,2,0,0];series[4]=[0,0,1,0,0,1];
The comment at the top is some background to alternative options I considered for creating the series. The first was to use formula language, @Sum(@DbLookup(... to calculate totals for each call type for each contract. The second was to use getAllEntriesByKey and sum the values in server-side javascript. However, because I'm using a view that is not expected to hold too many entries, I'm looping through all entries in the view, which I suspect should be quicker. If my view should ever grow to have so many entries that my code becomes efficient, the chances are I have also got a large number of lines on my chart, which means it will be too cluttered for it to be of any use to the users and needs splitting into separate charts anyway.
I begin by getting the first row from the view. I'm using a categorised view (which you can see by opening the sample database in Notes Client), so my first entry is a category row for the first client. I initialise a variable ("i") to increment the array index of the series, and a second variable ("test") to make sure that there are some calls for that client: if there are no calls, I don't want to create a line. I also then create a variable called "title". This will hold a second series, "seriesT", in the same format we used for previous charts, that will hold the client names to use in the legend.
Finally I get to the loop, and for each category I create an element of the "series" array, write the client name + "," to the "seriesT" string, and get the next sibling. Note I check to make sure ent is not the total row at the bottom of the view, which is also a sibling of the categories. After the loop is completed, I add title to the client-side javascript in result, first removing the trailing comma and replacing it with a closing "]". I close the script tag in result and then return the whole client-side javascript.
Creating the Chart
As before I add xp:panels to hold the chart and a script block to add an onLoad function, like so:
<xp:scriptBlock id="scriptBlock1">
<xp:this.value><![CDATA[makeCharts = function(){
var chart1 = new dojox.charting.Chart2D("#{id:simplechart}");
chart1.setTheme(dojox.charting.themes.PlotKit.blue);
/*
Lines/Areas/Markers @params
@lines: whether to show lines between the points on the chart (for areas, whether the fill the areas)
@markers: whether to show markers at the points
@tension: numeric degree with which to curve the lines
@shadows: styling for shadows relative to the lines. Takes three @params, e.g. {dx: 2, dy: 2, dw: 2}
@dx: offset x, positive is lower than the line
@dy: offset y, positive is right of the line
@dw: width of the shadow
*/
chart1.addPlot("default", {type: "Lines", lines: true, markers: true, tension: 3});
chart1.addPlot("Grid", {type: "Grid"});
// addAxis @params see other XPage for charts, additionally labels, an array of objects, value replaced with text
chart1.addAxis("x", {max:7, labels:
[{value:1, text:"New"}, {value:2, text:"Closed"}, {value:3, text:"Open"},
{value:4, text:"UAT"}, {value:5, text:"Maint"}, {value:6, text:"Long"}, {value:7, text:""}]
});
chart1.addAxis("y", {vertical: true});
var anim_e = new dojox.charting.action2d.Tooltip(chart1, "default");
for (i=0;i<series.length;i++) {
chart1.addSeries(seriesT[i], series[i]);
}
chart1.render();
var legendTwo = new dojox.charting.widget.Legend({
chart: chart1, horizontal: false
},
"#{id:Legend}");
}
XSP.addOnLoad(makeCharts);]]></xp:this.value>
</xp:scriptBlock>
Line charts, area charts, scatter graphs etc are all very similar and take the same parameters. The diference is the styling (if any) of what joins the markers. I have also added a grid and axes named "x" and "y". As I mentioned before, by default x and y are the axis names applied to any plot, so they are not allocated to the default plot. The default for any axis is "vertical: false", so for the y axis I set vertical to true.
For the x plot I force a max to give a bit of space at the right-hand side of the chart. This allows the user to see the whole marker assigned to Long, otherwise you could only see the left half of the marker image. I also override the labels, replacing "1" with "New", "2" with "Closed", "3" with "Open" etc., ensuring 7 is removed. Using the same idea you could override the y axis to include the word "calls" against each number.
Again, animations such as moveSlice do not work on a line chart, so I just use the tooltip, this time using the default value (the number of calls) that was put into the series. On this chart I feel it has benefit, to help see the actual number when the ticks are at every 10 calls.
I then add the series. You will remember that "series" contained an array of series, one relating to each client. Consequently I loop through that array calling chart1.addSeries for each element. The second parameter is the series itself. The first parameter is the series name, so I get the corresponding element of seriesT (the array of client names). This parameter is what will be used in the legend, which again I force to be vertical.
Line Chart via HTML Markup
In this part, I will create a line chart similar to the one created in the previous part, but this time using an HTML table data store and markup HTML. This took a bit of trial and error because, although there are plenty of examples on the web of charts created with javascript, the examples of charts created using HTML tend to only cover the basics.
As with the other charts, I add the dojo modules and code to force compatibility mode. With the javascript line chart I showed one line for each client. On this chart I am going to show one line for each contract and allow the user to select the client from a combobox.
<xp:comboBox id="custSelect" value="#{viewScope.custReport}">
<xp:selectItems>
<xp:this.value><![CDATA[\${javascript:@Unique(@DbColumn(@DbName(),"liveCallsByClientContract",1))}]]></xp:this.value>
</xp:selectItems>
<!--
On change, do a partial refresh of the panel, recalculating the view
-->
<xp:eventHandler event="onchange" submit="true"
refreshMode="partial" refreshId="chartPanel" onComplete="stoploading();">
<xp:this.script><![CDATA[loading();]]></xp:this.script>
</xp:eventHandler>
</xp:comboBox>
So my combobox is bound to a viewScope variable called custReport. It provides a list of clients and onchange triggers a partial refresh of the chartPanel xp:panel. The onchange calls the client-side javascript function loading() and the onComplete calls the corresponding function stoploading(), both in the loading client-side javascript library. This shows and then hides a dijit dialog underlay with a background image, to show the user that something is happening and prevent them clicking anywhere else on the screen. Thanks to Mark Hughes for blogging on how to do this, and it's a nice touch for making your applications look slick.
<xp:panel id="chartPanel">
<!--
Data is in the panel so we can refresh the keys exact match,
pulling from viewScope (if it exists)
-->
<xp:this.data>
<xp:dominoView var="view1" viewName="liveCallsByClientContract"
keysExactMatch="true">
<xp:this.keys><![CDATA[#{javascript:(viewScope.custReport == null) ? @Subset(@DbColumn(@DbName(),"liveCallsByClientContract",1),1) : viewScope.custReport}]]></xp:this.keys>
</xp:dominoView>
</xp:this.data>
<!--
repeat to create HtmlStore and table that the store uses, e.g.
tableStore1, tableStore2; tableExample1, tableExample2
-->
<xp:repeat var="rowData" indexVar="rowNo" value="#{view1}">
<div dojoType="dojox.data.HtmlStore" dataId="#{javascript:'tableExample' + rowNo}"
jsId="#{javascript:'tableStore' + rowNo}">
</div>
<table id="#{javascript:'tableExample' + rowNo}" style="display:none">
<thead>
<tr>
Then I have the chartPanel xp:panel, in which I put the reference to the view, the HTML table store and the chart. By specifying the view in the panel I'm refreshing onchange of the combobox I can just do a partial refresh only return the data I need. Note that the viewScope variable is null when you first open the page, so in that case I set the key to the first client in the view.
After the dominoView data definition, I have a repeat control to create for each contract for the current client a dojox.data.HTMLStore and an HTML table that the dojox.data.HTMLStore references. This is exactly the same technique used by Chris Connor, except I'm adding the rowNo to each tableStore and tableExample, because these are divs and tables, not xp:divs and xp:tables, so I need to make sure the ids are unique. After completing the table and closing the repeat control, I'm ready to start creating the chart itself.
<div dojoType="dojox.charting.widget.Chart2D" id="chart1"
style="width: 300px; height: 300px;" theme="dojox.charting.themes.PlotKit.blue">
<!-- Classes for axes, plot and grid -->
<div class="axis" name="x" max="6.5"
labels="[{value:1, text:'New'}, {value:2, text:'Closed'}, {value:3, text:'Open'},{value:4, text:'UAT'}, {value:5, text:'Maint'}, {value:6, text:'Long'}, {value:7, text:''}]">
</div>
<div class="axis" name="y" vertical="true"></div>
<div class="plot" name="default" type="Lines" lines="true"
markers="true" tension="3">
</div>
<div class="plot" name="grid" type="Grid"></div>
The first part is the div for the chart itself, defining the parameters - the style and the theme. If I understand the logic behind how the markup HTML works, it appears that within the div for the chart there are then additional divs for the different elements of the chart like the axes, the plots, the series, the animations etc.
Within the div for the chart the first elements I create are the two axes, x and y. These are divs with class set as axis. The names of the properties of the axes are the same as the javascript properties - as you'll see from the screenshot below of the javascript code I used in chart2.xsp.
As with the javascript version, the markup HTML expects a JSON object for the labels. (Note that when I tried an HTML table with a dojox.data.HTMLStore, it that did not work.) The only other difference is that the other parameters need to have text passed, otherwise you cannot save the XPage. After the axes I create divs with class="plot", one for the plot type to be used for the default series shown on the chart, the second to add a grid - again, all the parameter names are the same.
<!--
Another repeat to hold the series. We compute the name and store
-->
<xp:repeat var="rowData" indexVar="rowNo" value="#{view1}">
<div class="series" name="#{javascript:rowData.getColumnValues()[1]}"
store="#{javascript:'tableStore' + rowNo}" valueFn="Number(x)">
</div>
</xp:repeat>
<!-- Tooltips and animations are divs of class action -->
<div class="action" type="Tooltip"></div>
</div>
<!-- div for Legend widget. NOTE: chartRef here, not chart -->
<div dojoType="dojox.charting.widget.Legend" id="legend1"
chartRef="chart1" horizontal="false">
</div>
</xp:panel>
</xp:panel>
Next I add a repeat control to add the series. Within the repeat control I create a div with class="series". I'm binding it to the view I used when I created the dojox.data.HTMLStore divs, which allows me to put the contract name into the "name" parameter for each series and also to use rowNo to get the corresponding HTMLStore I created earlier. The format and parameters I have used for the series divs are as on the dojocampus website, but the valueFn parameter (which was not used in the javascript charts) is used in the dojox.charting.widget.Chart2D.js file in the buildRendering function. I think it creates an array of series, though my understanding of the javascript in the dojo files is somewhat limited.
Finally I add a div with class="action". This kind of div is used for the various types of animation, in this case I've defined this div as a Tooltip. Here I'm using the basic tooltip of the value returned from the series. After closing the chart's div I add the legend. Here one parameter is slightly different. Whereas in javascript the parameter was "chart", in markup HTML it's "chartRef".
Appendices
To view the original series, follow this link: http://hermes.intec.co.uk/Intec/Blog.nsf/archive?openview&title=Dojox%20Charting%20Tutorial&type=cat&cat=Dojox%20Charting%20Tutorial
To download the sample nsf: http://hermes.intec.co.uk/Intec/Blog.nsf/dx/Charts.zip/$file/Charts.zip
Obviously the first point of reference is the dojocampus article on charting: http://docs.dojocampus.org/dojox/charting. Bear in mind that the demos don't currently work in Internet Explorer 8.
A useful reference for all the parameters etc is the dojo api website: http://api.dojotoolkit.org/jsdoc/1.3.2/dojox.charting.Chart2D
The dojocampus article uses and references invaluable articles previously written by Doug McMaster and Eugene Lazutkin on Sitepen: http://www.sitepen.com/blog/2008/06/06/a-beginners-guide-to-dojo-charting-part-1-of-2/
From an XPages point of view, my starting point was Chris Connor's very useful article: [404 Not Found] www.xsptalk.com/public/website/blog.nsf/dx/13112009111014CCOF4X.htm
Only in the last couple of days I also came across Glen Urban's excellent article on dojox charting in classic web design, which also uses the javascript method of creating the chart: [404 Not Found] glenurban.me.uk/A55D03/Blog.nsf/dx/DojoChart.htm
And just yesterday, Lance Spellman, with Jeremy Hodge, have shown how the config for a chart could be passed to a standard custom control for a chart type http://xpages.info/XPagesBlog.nsf/dx/update-self-awareness-of-custom-controls-in-xpages-am-i-alon.html
Mark Hughes' article on images and masking is here: http://dominoextnd.blogspot.com/2009/11/xpages-loading-images-and-masking.html
If you want to use dojox charting via the web but your server is not yet 8.5.1, I would recommend using Dojomino, a database comprising all the dojo 1.3.2 files. Even with an 8.5.1 server, I still find it useful because it provides a readable version of the javascript files (the ones that come with the Notes Client install are compressed down to a single line): [404 Not Found] www.dojomino.com/
The code provided by Eugene Lazutkin to extend the tooltips for bar and column charts was a god-send: [404 Not Found] dojotoolkit.org/forum/dojox-dojox/dojox-support/custom-tooltips-clusteredbars-chart
Just this week Bob Balfe blogged about a variety of options for charting, and others in the community have expanded this into an excellent resource that there is no point in reproducing here: http://blog.balfes.net/?p=989. One option I would like to mention though is the charting sidebar plugin Julian Robichaux and Rob McDonagh showed at Lotusphere 2009, which I've had in my Notes Client for some time now.
I apologise for any link I have not mentioned. If you're aware of a useful resource, by all means add a comment.
About the Author
Paul Withers is a Domino Developer at Intec Systems Ltd. The original post of this and other tips / observations / tutorials are available at the
Intec Blog