Friday, December 11, 2009

Calling Client Side JavaScript from a BIRT Chart

A couple of months ago I detailed a new feature for BIRT charts that allows multiple hyperlinks to be attached to one the supported events. That post is available here.

In this post I will discuss using a BIRT Text element that contains script which executes within the client browser and contains functions that are called from rendered charts.

General Information
BIRT currently supports interactivity on many chart components like chart series, title, axis, and the legend. The components that support interactivity will depend on the type of chart being used. The events are client based events like mouse click, mouse over, key down, etc. Multiple events can be hooked and each is associated with an action. Actions define the behavior that should occur when a specific event happens. The actions available depend on what component the action is defined for and what chart output type is being used. Currently BIRT supports the following Actions.

Hyperlink – Supports multiple hyperlinks, drill through, and linking to bookmarks.
Show Toolip – Supports displaying tooltips. Data available for use in the tooltip will depend on the component.
Highlight – Highlights the selected component. Most often used to highlight specific series or data point.
Toggle Visibility – Toggles the visibility of the selected component. Most often used to change the visibility of a series or data point.
Toggle DataPoint Visibility – Toggles the data point label visibility.
Invoke Script – Invokes client side script.

Additionally if you are using the Chart engine within an SWT, SVG, or Swing based application the engine supports adding a callback action that your code can use to interpret chart events.



Highlight, Toggle Visibility, and Toggle DataPoint Visibility actions are only available when using an SVG output setting.



If you use SVG, and you wish to test the report in the designer remember to set the Enable SVG chart in the Preview preferences for the Report Design Preference entry.



More information on Chart Interactivity is available here. Also see the Chart FAQ for more details.

InvokeScript Example
The follow section details an example of using the invokeScript action on multiple Chart events.

Assume that you have a Chart that displays a set of customers with a series value for each equal to the customer's credit limit.



Suppose you want to display the customer details right below the chart when the mouse is moved over a specific data point. This can be done using the invokeScript action on the mouse over event for the chart series. To do this first create a table below the chart that is bound to the same dataset that the chart is using. You can then nest a table that calls another data set to retrieve customer information. In the attached example this will be the ChartData and CustomerInformation datasets respectively.



By default this will generate a inner table that contains customer information for every customer represented in the chart. Obviously you do not want to display all of these at once, so create a new style in the style editor with only one property overridden – Text Block:Display set to no Display. Apply this new style to both the inner and outer tables.



If you run the report after completing this step all the tables will be generated in the output but none will be displayed. This is different than using the visibility property which will not put the tables in the output. Enter the following bookmarks for the outer and inner tables.

Outer table bookmark expression: "myoutertable";
Inner table bookmark expression: "mytable"+row["CUSTOMERNUMBER"]

This will assign the outer table id to myoutertable and a unique id for each inner table starting with mytable and the customer number for the given inner table appended to it. We can now use these with some client script to turn them on or off.

Add a Text element below the two tables with the following value.


<script>
function clearSel(){
var ot=document.getElementById("myoutertable");
ot.style.display="none";
var intbls=ot.getElementsByTagName("TABLE");
for( jj=0; jj<intbls.length; jj++ ){
intbls[jj].style.display = "none";
}
}
function DisplayCustomer(cat) {
clearSel();
//alert(cat);
document.getElementById("myoutertable").style.display="block"
document.getElementById("mytable"+cat).style.display="block"

}
</script>



Make sure to set the Text type to HTML.

The clearSel function first finds the outer table and then locates every nested table and sets the display style for each to none. The outer table’s display style is also set to none. This will effectively hide both tables.

The DisplayCustomer function first clears all current displayed tables. Next it uses a category passed in from the Chart to find the specific customer inner table and sets its display style to block. It also sets the outer table to be visible.

To link these two functions to the Chart use the Interactivity button on the Value (Y) Series as shown in the picture below. Note that we use a mouse over event to call the DisplayCustomer function and a mouse click function to clear the tables.



This produces the following output.



As the mouse is moved over the different tubes the table below the chart will update. To clear the tables click on one of the tubes. You will notice that we passed categoryData to the DisplayCustomer JavaScript function. This is a predefined variable available to the invokeScript function that contains the category value for a specific datapoint. The example report lists the predefined variables and in what action they are available using label elements at the top of the report. These labels are hidden at run time.

The example is available at BIRT Exchange.

25 comments:

charu said...

how to use valueSeriesName to get the series name in bar chart. I get valueSeriesName is undefined when i try to use it

Jason Weathersby said...

The variable is
valueSeriesName. If you wanted to modify the above example you would change the script:
function DisplayCustomer(cat, vsn) {
clearSel();
alert(vsn);

and in the the chart name the series (third tab) whatever, and modify the script on the chart to look like this:

DisplayCustomer(categoryData, valueSeriesName);

charu said...

i am using birt 2.3.2 version. I used the same variable name 'valueSeriesName' but without any success. It gives error - valueSeriesName is undefined in IE

Jason Weathersby said...

If you put a mouse over event on the bar and set the action to invokeScript and put in

alert(valueSeriesName);

does it work?

charu said...

I tried the same thing and get the error that valueSeriesName is undefined error

Jason Weathersby said...

I tried this on 2.3.2 and it worked for me. Do you have the series named?

charu said...

i am using the optional Y-grouping and need to get that series name when i hover over datapoint

Jason Weathersby said...

I logged a bug for this:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=298953

As a work around you should be able to build an expression for the toolip that uses the values defined for the chart like:
row["PRODUCTCODE"]+"-"+row["ORDERLINENUMBER"]+"-"+row["QUANTITYORDERED"]

product code is my category
Order line number is my optional y grouping and quantity ordered is my y series value.

charu said...

i need to invoke a script with the series name so that it highlights the corresponding row in a table

Jason Weathersby said...

The invoke script worked for me with optional y grouping. Can you reproduce the issue with the sample db and email me the report to jasonweathersby at windstream.net

Unknown said...

JavaScript is a good program and very easy to use. I don´t like a complex program. I prefer javascript because i consider it like a device very eficient and it have a good quality.
I always looking for the quality that is why i prefer to buy viagra because i always have a great result in my sexual life.

Eric Hougen said...

I built the report in 2.5.2 against the model db, trying to do it exactly as described. At the final steps, I am unable to get the table to display on mouse-over, but there are no errors. The chart shows up fine. I can get tooltips to show on mouse-over, but i'd like to get the table to show up. I did apply the style to both tables, and inserted the script below the tables and have the parameter in the customer info dataset. Anybody got any idea where this could have gone wrong?

Jason Weathersby said...

Did the sample run for you? I just tried it in 2.5.2 and it worked fine. What browser are you using?

Eric Hougen said...

Jason - thanks. Yes, the samples runs fine in 2.5.2. I'm running the sample side-by-side with the the report I was trying to create from the instructions. I cannot find a single difference between the sample and my report.

I'm using IE 7.0. I did the chart as PNG.

It seems like there are several components to this. There's the chart, the table, the table parameter, the style, the script, the bookmarks, and the interactivity. I built it iteratively and got the chart and table, the style and the parameter working. The interactivity seems to work since I can get it to work for tooltips. So that leaves the bookmarks and the script. I don't get an error. I copied the script and the bookmark word-for-word from your blog entry. So I'm stumped.

One thing I'm not clear on is how the chart passes the parameter value to the table. I was forced to create a default value on the parameter and I dont get how that is overcome.

Jason Weathersby said...

In the mouse over event I put this call in:
DisplayCustomer(categoryData, valueSeriesName);

These are predefined variables in charts. The categoryData is the cat value for the particular point.
Take a look at this post:
http://birtworld.blogspot.com/2010/05/more-on-chart-interactivity.html

Eric Hougen said...

Jason - after your last post directing me to another article, I got the mouse over alert to work first try. The alert flashes the category data and value data correctly. Sorry to ask, but if you had to guess, where am I going wrong in replicating the sample? I was a little confused by your last post in which you said that the mouseover event is DisplayCustomer(categoryData, valueSeriesName); and your article you'd said it was DisplayCustomer(categoryData); Maybe that doesnt matter but it was something I noticed. -Thanks Eric

Eric Hougen said...

one more thing...I removed the styles from both tables and the mouse over works fine. When the report runs, all nested tables appear. As I mouse through the chart columns, the different nested tables appear and disappear. When I click, all tables disappear.

Jason Weathersby said...

On the displaycustomer function I probably changed my local copy and did not look at what I posted. Can you send me the sample you are creating? jasonweathersby at windstream dot net.

Anonymous said...

Hello,
i have a report where i have option paramter user name , now if user name is "ALL" i wish to have drill through option on user name column show that details related to that user can be shown and when paramter is not ALL (which means requester asked for specific user), idont want to have drill through ,

Summary: How to make column drill through on the basis of some condition (and vice versa).

Please help !!

Jason Weathersby said...

Do you mean drill through to another report? do you want change the drill operation on a chart or a normal report item?

Anonymous said...

Do you mean drill through to another report?
It will be drill through on same report (same report will be opened on clicking of user name)

do you want change the drill operation on a chart or a normal report item?
its normal report(without chart)

Luke Matthews said...

Hi, I can get the invoke script to show an alert for "axisLabel". Am I able to use that as part of a drill through parameter value?

<structure>
<property name="linkType">drill-through</property>
<property name="reportName">detail.rptdesign</property>
<property name="targetWindow">_blank</property>
<property name="targetFileType">report-design</property>
<list-property name="paramBindings">
<structure>
<property name="paramName">report_date</property>
<simple-property-list name="expression">
<value type="javascript">params["report_date"].value</value>
</simple-property-list>
</structure>
<structure>
<property name="paramName">area</property>
<simple-property-list name="expression">
<value type="javascript">axisLabel</value>
</simple-property-list>
</structure>
</list-property>
</structure>

Jason Weathersby said...

Luke,

Currently I think that is a bug and you can not put it in a drill through. You could always use invokeScript and write your own drill through.

Jason

Cooper said...

Thank you so much, Jason!
You posted a great tutorial with good examples. To me even the use of ValueSeriesName got way easier to understand.

Unknown said...

Why we need outer table here..did not get the idea. I have the same requirement but not able to implement. It is showing all the records instead of the selected one on the chart.

Any help would be appreciated.
Thanks.