Pentaho CDE: Global Properties

  23 Mar 2015


This article is a brief walkthrough on how to define global ** Chart Properties**. We start with some simple examples and work our way towards creating these global properties.

Replacing CDE Chart Properties (or: How to copy JavaScript code directly into CDE)

Sometimes when you prototype your chart outside CDE in plain HTML you would like to just copy the JavaScript chart definition into CDE without having to fill out all the properties.

It is indeed possible to do this by overriding the chartDefinition object in the PreExecution function.

It is best that you familiarise yourself first with all the properties of the chartDefintion object using the PreExecution function:

function(){
	console.log(this.chartDefinition);
}

You will also see that there are a few additional properties like dataAccessId (to define the query), which are not available in standard .

Our code in JavaScript looks like this (partial extract only):

...
new pvc.LineChart({
  canvas: "cccExample",
  width:   600,
  height:  200,
  animate: false,
  timeSeries: true,
  timeSeriesFormat: "%Y-%m-%d"
})
...

In this case in CDE we add a ** Line Chart** component (so later on we do not have to define the chart type in our code again). We set the standard properties like Name, Datasource, HTMLObject, crosstabMode and seriesInRows. Then we can add our JavaScript in CDE to the PreExecution function like this:

function(){
	// override the CDE chart definition
    this.chartDefinition = {
      width:   600,
      height:  200,
      animate: false,
      timeSeries: true,
      timeSeriesFormat: "%Y-%m-%d",
      dataAcccessId: 'qry_test'
    }; 

} 

Note: CDE sets default values for quite some chart properties. This is why we override the complete CDE chart definition.

Integrating Extension Points is a bit more work because they are stored as an array within CDE whereas in ** it is defined as an **object. We will take a look at this in the next section. UPDATE: You can define Extension Points on the same level as the standard properties (so very easily), but there is a small caveat to this (read more about it later on).

Extending CDE Chart Properties

Instead of completely overriding the chart definition, we sometimes just want to extend it: If there are common properties, we want to override them, otherwise we want to keep the current value.

Integrating Extension Points is a bit more work because they are stored as an array within CDE whereas in ** it is defined as an **object (usually):

function(){
	// get chart definition
	var cccOptions = this.chartDefinition;
	
	// standard ccc properties
	var myCccProperties = {
		titleFont: 'bold 16px Verdana, Geneva, sans-serif'
		, baseAxisTitleFont: 'bold 14px Verdana, Geneva, sans-serif'
		, legendFont: '14px Verdana, Geneva, sans-serif'
		, orthoAxisTitleFont: 'bold 14px Verdana, Geneva, sans-serif'
		, valuesFont: '10px Verdana, Geneva, sans-serif'	
	};
	
	// Add/override  options
	$.extend(cccOptions, myCccProperties);
	
	// extension points
	// are stored in CDE as array and not as an object like in 
	var extensionPoints = Dashboards.propertiesArrayToObject(cccOptions.extensionPoints);
	
	var myCccExtensionPoints = {
		orthoAxisTitleLabel_textStyle: 'white'
		, xAxisLabel_textStyle: 'white'
		, yAxisLabel_textStyle: 'white'
		, legendLabel_textStyle: 'white'
		, axisLabel_font: '12px Verdana, Geneva, sans-serif'
		, axisRule_strokeStyle: 'white'
		, axisOffset: 0
		, line_interpolate: 'monotone'
		, area_interpolate: 'monotone'	
	}
	
	// Add/override  options
	$.extend(extensionPoints myCccExtensionPoints);
	
	// Update the CDE extension points list with the new settings
	cccOptions.extensionPoints = Dashboards.objectToPropertiesArray(extensionPoints);
} 

However, an improvement was implemented some time ago, which allows you to define extension points as a property of the chartDefinition instead of using the CDE custom chartDefinition.extensionPoints property. Note that if there are any properties defined in CDE chartDefinition.extensionPoints (e.g. via the UI), these ones will have priority over the ones defined directly in chartDefinition:

function(){
	// get chart definition
	var cccOptions = this.chartDefinition;

	// standard ccc properties
	var myCccProperties = {
		titleFont: 'bold 16px Verdana, Geneva, sans-serif'
		, baseAxisTitleFont: 'bold 14px Verdana, Geneva, sans-serif'
		, legendFont: '14px Verdana, Geneva, sans-serif'
		, orthoAxisTitleFont: 'bold 14px Verdana, Geneva, sans-serif'
		, valuesFont: '10px Verdana, Geneva, sans-serif'	
		// extension points
		, orthoAxisTitleLabel_textStyle: 'white'
		, xAxisLabel_textStyle: 'white'
		, yAxisLabel_textStyle: 'white'
		, legendLabel_textStyle: 'white'
		, axisLabel_font: '12px Verdana, Geneva, sans-serif'
		, axisRule_strokeStyle: 'white'
		, line_interpolate: 'monotone'
		, area_interpolate: 'monotone'	
	};
	
	// Add/override  options
	$.extend(cccOptions, myCccProperties);
}

CDE: Global Properties

When creating a dashboard with various charts, usually certain chart properties are the same across all the charts. You might also want to achieve a certain design across all your charts. In this case, it would be ideal to define these properties globally.

Let’s see how this can be done with ** in **CDE:

Create a JavaScript file with the basic settings used across all charts. Encapsulate this in a function. E.g.:

function initGlobalCccProperties(chartDefinition){
	// get chart definition
	// var cccOptions = this.chartDefinition;
	var cccOptions = chartDefinition;

	// standard ccc properties
	var globalCccProperties = {
		titleFont: 'bold 16px Verdana, Geneva, sans-serif'
		, baseAxisTitleFont: 'bold 14px Verdana, Geneva, sans-serif'
		, legendFont: '14px Verdana, Geneva, sans-serif'
		, orthoAxisTitleFont: 'bold 14px Verdana, Geneva, sans-serif'
		, valuesFont: '10px Verdana, Geneva, sans-serif'	
		// extension points: A recent improvement allows you to
		// set them directly as a property of the chart definition
		// instead of using the custom CDE chartDefinition.extensionPoints
		// however if there are any properties defined in
		// chartDefinition.extensionPoints, they will take priority
		// over these ones.
		, orthoAxisTitleLabel_textStyle: 'white'
		, xAxisLabel_textStyle: 'white'
		, yAxisLabel_textStyle: 'white'
		, legendLabel_textStyle: 'white'
		, axisLabel_font: '12px Verdana, Geneva, sans-serif'
		, axisRule_strokeStyle: 'white'
		, axisOffset: 0
		, line_interpolate: 'monotone'
		, area_interpolate: 'monotone'	

	// Add/override  options
	$.extend(cccOptions, globalCccProperties);	
} 

As noted previously, defining extension points as direct properties of chartDefinition will not override any extension points set in the CDE specific chartDefinition.extensionPoints. If you want to override these ones as well, then you can use this approach:

function initGlobalCccProperties(chartDefinition){
	// get chart definition
	// var cccOptions = this.chartDefinition;
	var cccOptions = chartDefinition;
	
	// standard ccc properties
	var globalCccProperties = {
		titleFont: 'bold 16px Verdana, Geneva, sans-serif'
		, baseAxisTitleFont: 'bold 14px Verdana, Geneva, sans-serif'
		, legendFont: '14px Verdana, Geneva, sans-serif'
		, orthoAxisTitleFont: 'bold 14px Verdana, Geneva, sans-serif'
		, valuesFont: '10px Verdana, Geneva, sans-serif'	
	};
	
	// Add/override  options
	$.extend(cccOptions, globalCccProperties);
	
	// extension points
	// are stored in CDE as array and not as an object like in 
	var extensionPoints = Dashboards.propertiesArrayToObject(cccOptions.extensionPoints);
	
	var globalCccExtensionPoints = {
		orthoAxisTitleLabel_textStyle: 'white'
		, xAxisLabel_textStyle: 'white'
		, yAxisLabel_textStyle: 'white'
		, legendLabel_textStyle: 'white'
		, axisLabel_font: '12px Verdana, Geneva, sans-serif'
		, axisRule_strokeStyle: 'white'
		, axisOffset: 0
		, line_interpolate: 'monotone'
		, area_interpolate: 'monotone'	
	}
	
	// Add/override  options
	$.extend(extensionPoints, globalCccExtensionPoints);
	
	// Update the CDE extension points list with the new settings
	cccOptions.extensionPoints = Dashboards.objectToPropertiesArray(extensionPoints);	
}

In your CDE dashboard include this JavaScript file as an external resource.

Then we can add our function ( e.g. initGlobalCccProperties) in the CDE Chart Component to the PreExecution function like this:

function(){
    initGlobalCccProperties(this.chartDefinition); 
}

Now this chart component will make use of the global settings.

Duarte Cunha Leão, lead architect of **, provides following advice:

Other caveats also exist in cases where the intended global style extension behaviour, or that of subsequent local styles, would be to “add” instead of “replace”. Extension points like mark_event, mark_add, mark_call, action handlers, like clickAction, selectionChangedAction, etc, are usually meant to have an “additive” behaviour. For extension points, there’s a syntax that simultaneously supports extension points with more than one argument and calling them more than once:

"mark_event": [
    ["point",     function() { }] // 2 arguments of 1st ep call
    ["unpoint", function() { }] // 2 arguments of 2st ep call
]

So, you could, in principle, have an chartDefinition extend method that would be smart enough to combine these additive-like extension properties properly. For the action handlers, the only way is for you to wrap a function that calls the previous value, and the new value. However, because of multiple executions, note that what you set in the chart definition in one execution, is still there in the following execution. So you have to have additional care to not combine with previous values, or in branching situations where you set options in one case but not in the following - the previous execution’s option values remain there - you might need to reset them.

Some more info about this can be found here.

The basic idea is that an extension point can listen to more than one event (e.g. mouseover and contextmenu). This is achieved by using a special syntax for the extension point, basically defining an array with functions:

bar_event: [
    ['contextmenu', function(s) { alert('context ' + s.vars.category.value); }],
    ['mouseover',   function(s) { alert('over '    + s.vars.category.value); }]
]
comments powered by Disqus