EXTJS 4 - I am trying to customize the renderer function for the 'series' in StackedBarChart. I want to conditionally color the bars.
renderer: function(sprite, record, curAttr, index, store) {
return Ext.apply(curAttr, {
fill: color
});
return curAttr;
},
My Question is, how to find out which element its currently rendering. I want to give white color to the first element of each record in my data store/series.
Thank you.
I found a way to detect exactly which element is currently being rendered. First, you'll need the following override, which addresses several issues with the renderer parameters. It shouldn't affect the normal bar charts, but I haven't tested them.
Ext.override(Ext.chart.series.Bar,{
drawSeries: function() {
var me = this,
chart = me.chart,
store = chart.getChartStore(),
surface = chart.surface,
animate = chart.animate,
stacked = me.stacked,
column = me.column,
enableShadows = chart.shadow,
shadowGroups = me.shadowGroups,
shadowGroupsLn = shadowGroups.length,
group = me.group,
seriesStyle = me.seriesStyle,
items, ln, i, j, baseAttrs, sprite, rendererAttributes, shadowIndex, shadowGroup,
bounds, endSeriesStyle, barAttr, attrs, anim;
// ---- start edit ----
var currentCol, currentStoreIndex;
// ---- end edit ----
if (!store || !store.getCount()) {
return;
}
//fill colors are taken from the colors array.
delete seriesStyle.fill;
endSeriesStyle = Ext.apply(seriesStyle, this.style);
me.unHighlightItem();
me.cleanHighlights();
me.getPaths();
bounds = me.bounds;
items = me.items;
baseAttrs = column ? {
y: bounds.zero,
height: 0
} : {
x: bounds.zero,
width: 0
};
ln = items.length;
// Create new or reuse sprites and animate/display
for (i = 0; i < ln; i++) {
sprite = group.getAt(i);
barAttr = items[i].attr;
if (enableShadows) {
items[i].shadows = me.renderShadows(i, barAttr, baseAttrs, bounds);
}
// ---- start edit ----
if (stacked && items[i].storeItem.index != currentStoreIndex) {
//console.log("i: %o, barsLen: %o, j: %o, items[i]: %o",i,bounds.barsLen,i / bounds.barsLen,items[i]);
currentStoreIndex = items[i].storeItem.index;
currentCol = 0;
}
else {
++currentCol;
}
// ---- end edit ----
// Create a new sprite if needed (no height)
if (!sprite) {
attrs = Ext.apply({}, baseAttrs, barAttr);
attrs = Ext.apply(attrs, endSeriesStyle || {});
sprite = surface.add(Ext.apply({}, {
type: 'rect',
group: group
}, attrs));
}
if (animate) {
// ---- start edit ----
rendererAttributes = me.renderer(sprite, items[i].storeItem, barAttr, (stacked? currentStoreIndex : i), store, (stacked? currentCol : undefined));
// ---- end edit ----
sprite._to = rendererAttributes;
anim = me.onAnimate(sprite, { to: Ext.apply(rendererAttributes, endSeriesStyle) });
if (enableShadows && stacked && (i % bounds.barsLen === 0)) {
j = i / bounds.barsLen;
for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) {
anim.on('afteranimate', function() {
this.show(true);
}, shadowGroups[shadowIndex].getAt(j));
}
}
}
else {
// ---- start edit ----
rendererAttributes = me.renderer(sprite, items[i].storeItem, Ext.apply(barAttr, { hidden: false }), (stacked? currentStoreIndex : i), store, (stacked? currentCol : undefined));
// ---- end edit ----
sprite.setAttributes(Ext.apply(rendererAttributes, endSeriesStyle), true);
}
items[i].sprite = sprite;
}
// Hide unused sprites
ln = group.getCount();
for (j = i; j < ln; j++) {
group.getAt(j).hide(true);
}
// Hide unused shadows
if (enableShadows) {
for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) {
shadowGroup = shadowGroups[shadowIndex];
ln = shadowGroup.getCount();
for (j = i; j < ln; j++) {
shadowGroup.getAt(j).hide(true);
}
}
}
me.renderLabels();
}
});
Here's the change list for renderer()
:
{name: 'metric': segment1: 12, segment2: 22}
, the index for segment1
will be 0
instead of 1
, because the first item in the record does not belong to its axis (it's the category name)So, to answer your question, now you can use the renderer like so:
renderer: function(sprite, record, attr, storeIndex, store, col) {
// set the color to white for the first item in every record
return (col == 0)? Ext.apply(attr, { fill: '#fff' }) : attr;
}
If you want to set the color for a named item, you may also do it like this:
// let's say that every record looks like this:
// {name: 'metric one', user1: 23, user2: 50, user3: 10}
renderer: function(sprite, record, attr, storeIndex, store, col) {
// retrieve the segment property name from the record using its numeric index.
// remember that 'col' doesn't take into account other fields that don't
// belong to the axis, so in this case we have to add 1 to the index
var uid = Ext.Object.getAt(record.data, col+1)[0];
// set the color to red for 'user2'
return (uid == 'user2')? Ext.apply(attr, { fill: '#f00' }) : attr;
}
For that last one, you'll need this function, which allows you to retrieve a property from an object using a numeric index:
/**
* Returns the key and its value at the idx position
* @return {Array} Array containing [key, value] or null
*/
Ext.Object.getAt = function(obj, idx) {
var keys = Ext.Object.getKeys(obj);
if (idx < keys.length) {
return [keys[idx],obj[keys[idx]]];
}
return null;
}
The index tells you which element is rendering. I've noticed however that in some situations renderer() is called 3 times before the elements start getting processed. I asked in the Sencha forums about this but to no avail.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With