Give a different color to each category item on Sencha Bar Chart - extjs

I'm kind of stuck with this thing. What I want to do is to give each bar on a sencha chart a different color. This is what I have so far:
And this is my code for it:
Ext.setup({
tabletStartupScreen: 'tablet_startup.jpg',
phoneStartupScreen: 'phone_startup.jpg',
tabletIcon: 'icon-ipad.png',
phoneIcon: 'icon-iphone.png',
glossOnIcon: false,
onReady: function() {
Ext.regModel('Retail', {
fields: [
{name: 'id', type: 'string'},
{name: 'quantity', type: 'int'}
]
});
var retailStore = new Ext.data.JsonStore({
model: 'Retail',
proxy: {
type: 'ajax',
url: 'getData.php',
reader: {
type: 'json',
}
},
autoLoad: true
});
console.log(retailStore);
new Ext.chart.Panel({
id: 'chartCmp',
title: 'Stock Example',
fullscreen: true,
dockedItems: {
xtype: 'button',
iconCls: 'shuffle',
iconMask: true,
ui: 'plain',
dock: 'left'
},
items: {
cls: 'stock1',
theme: 'Demo',
legend: {
position: {
portrait: 'right',
landscape: 'top'
},
labelFont: '17px Arial'
},
interactions: [{
type: 'panzoom',
axes: {
left: {
maxZoom: 2
},
bottom: {
maxZoom: 4
}
}
}],
animate: false,
store: retailStore,
axes: [{
type: 'Numeric',
position: 'bottom',
fields: ['quantity'],
title: 'Quantity'
}, {
type: 'Category',
position: 'left',
fields: ['id'],
title: 'Products'
}],
series: [{
type: 'bar',
axis: 'right',
xField: 'id',
yField: ['quantity'],
}]
}
});
}});
I know there should be some way to "cheat" the chart by adding an extra dimension to it, just as it is done here:
http://dev.sencha.com/deploy/touch-charts-1.0.0/examples/Bar/
There each year represents a new product. I'd like to do the same with mine, each product representing a different dimension.

You can use the renderer function of a serie. You just have to change attributes.fill to the color you want for each bar. Here is an example : http://bl.ocks.org/3511876 and the code :
Ext.setup({
onReady: function() {
var data = [];
for (var i = 0; i < 10; i++) {
data.push({
x: i,
y: parseInt(Math.random() * 100)
});
}
var colors = ['blue', 'yellow', 'red', 'green', 'gray'];
var store = new Ext.data.JsonStore({
fields: ['x', 'y'],
data: data
});
var chart = new Ext.chart.Chart({
store: store,
axes: [{
type: 'Category',
fields: ['x'],
position: 'left'
}, {
type: 'Numeric',
fields: ['y'],
position: 'bottom'
}],
series: [{
type: 'bar',
xField: 'x',
yField: 'y',
axis: 'bottom',
renderer: function(sprite, record, attributes, index, store) {
attributes.fill = colors[index%colors.length];
return attributes;
}
}]
});
new Ext.chart.Panel({
fullscreen: true,
chart: chart
});
chart.redraw();
}
});

Related

Extjs line graph

I want to visualize different company data in different colors against date of a line graph.
The problem is the number of companies will change.
Let's say the input data will be like
data: [{ date: '2018-12-20', company1: 10, company2: 5, },{ date: '2018-12-21', company1: 10, company2: 10 }]
To visualize it the model will be like
Ext.define('ABC.model.Job', {
extend: 'Ext.data.Model',
fields: [
{name: 'DATE', type: 'auto'}
{name: '1', type: 'int'}
{name: '2', type: 'int'}
],
proxy: {
type: 'ajax',
noCache: false,
actionMethods: {'read': 'POST'},
api: {
read: utils.createUrl('api', 'read'),
},
reader: {
type: 'json',
root: 'data'
},
listeners: {
exception: function(proxy, response, operation) {
App.showHttpError('Job ', response);
}
}
}
});
And the view portion of axes will be
Ext.define('ABC.view.Job', {
extend: 'Ext.container.Container',
requires: [
'ABC.store.Job',
],
border: false,
layout: {type:'vbox', pack:'start', align:'stretch'},
initComponent: function() {
var me = this;
me.jobStore2 = Ext.create('ABC.store.Job');
Ext.apply(me, {
items: [
{
xtype: 'chart',
store: me.jobStore2,
style: 'background: #fff',
insetPadding: 40,
animate: true,
shadow: false,
flex: 2,
minHeight: 400,
legend: {
position: 'top',
boxStrokeWidth: 0,
labelFont: '12px Helvetica'
},
axes: [{
type: 'Numeric',
position: 'left',
fields: ['1'],
grid: true,
minimum: 0,
}, {
type: 'Category',
position: 'bottom',
fields: ['DATE'],
grid: true,
}],
series: [{
type: 'line',
axis: 'left',
title: '1',
xField: 'DATE',
yField: '1',
style: {
'stroke-width': 4
},
},
{
type: 'line',
axis: 'left',
xField: 'DATE',
border: false,
flex:1,
title: ['2'],
yField: ['2'],
style: {
'stroke-width': 4
},
}
]
}
});
me.callParent(arguments);
}
});
What if the data contains lots of companies. How can I change the series? Instead of giving the detail of y axis again and again
For ease of maintenance, you can just create an array containing the company names, and map it into both fields and axes:
var companies = ['company1', 'company2', ...];
Ext.create('Ext.data.Store', {
fields: [{
name: 'DATE',
type: 'auto'
}].concat(
companies.map(function(companyName) {
return {
name: companyName,
type: 'int'
};
})
)
});
...
series: companies.map(function(companyName) {
return {
type: 'line',
axis: 'left',
title: '1',
xField: 'DATE',
yField: companyName,
style: {
'stroke-width': 4
},
}
});
...

Match a serie in a chart with ComponentQuery

I'm using ExtJS 4 with MVC. I have a chart with two series: line and column. When clicking a column I need to trigger an event.
In the controller, I used the following query to match the column series, but doesn't work.
this.control({
'#companyChartItemId[series[type=column]]': {
itemmouseup:this.onItemMouseUp
}
});
Any thoughts how could I match it?
Below is my code:
app/view/company/Chart.js
Ext.define('Market.view.company.Chart', {
extend: 'Ext.chart.Chart',
xtype: 'companyChart',
requires: ['Market.store.HistoricalMarketData'],
theme: 'Category1',
store:'HistoricalMarketData',
initComponent: function() {
this.callParent();
},
axes: [{
type: 'Numeric',
position: 'left',
fields: ['close'],
title: 'Price',
grid: true
}, {
type: 'Numeric',
position: 'right',
fields: ['volume'],
title: 'Volume',
grid: true
}, {
type: 'Category',
position: 'bottom',
fields: ['time'],
title: 'Month of the Year'
}],
series: [{
type: 'column',
axis: 'right',
xField: 'time',
yField: 'volume',
highlight: true,
markerConfig: {
type: 'cross',
size: 3
}
}, {
type: 'line',
axis: 'left',
smooth: false,
fill: false,
fillOpacity: 0.8,
xField: 'time',
yField: 'close'
}]
});
app/controller/ChartController.js
Ext.define('Market.controller.ChartController', {
extend:'Ext.app.Controller',
stores:['MarketData'],
init: function() {
this.control({
'#companyChartItemId[series[type=column]]': {
itemmouseup:this.onItemMouseUp
}
});
},
onItemMouseUp: function (item) {
//do something
}
});
I would just configure the listener/response function where you define your series. You can add listeners as a config in your series, check out the doc:
http://docs.sencha.com/extjs/4.2.1/#!/api/Ext.chart.series.Series-cfg-listeners

How to make an Extjs4.2 Chart filtered by a list

I'm pretty new to web development and ExtJS, I've looked around for tutorials on more complex concepts but they are hard to find (Yes I've found the Sencha documentation but the only provide simple concepts)
I'm trying to make a dashboard for my application, in this dashboard I want a chart
that gives a detailed overview of my statistics. In order to accomplish this I am trying to add a custom filter for the chart by making a seperate list with that holds all the instances of the key, when selecting said instances from a list I want the chart to only display those selected instances.
The chart I made:
Ext.define('view.chart.LocationStatisticsPerMonth', {
extend: 'Ext.chart.Chart',
alias: 'widget.LocationStatisticsPerMonthChart',
requires: [
'store.LocationStatisticsStore',
'store.TemplateStore',
'view.TitleToolbar'
],
constructor: function () {
this.store = Ext.create('store.LocationStatisticsStore', {
autoLoad: true, sorters: [{
property: 'YearMonth',
direction: 'ASC'
}]
});
this.store.getProxy().api.read = ServerControllers.Dashboard.LocationStatisticsPerMonth;
this.callParent(arguments);
},
animate: true,
shadow: true,
border: true,
legend: {
position: 'right'
},
axes: [{
type: 'Numeric',
position: 'left',
fields: ['TotalCount'],
title: false,
grid: true,
label: {
renderer: function (v) {
return String(v);
}
}
}, {
type: 'Category',
position: 'bottom',
fields: ['Location_Id'],
title: false,
label: {
rotate: {
degrees: 315
}
}
}],
series: [{
type: 'column',
axis: 'left',
gutter: 80,
xField: ['Location_Id'],
yField: ['TotalCount'],
tips: {
trackMouse: true,
width: 125,
height: 28,
renderer: function(storeItem, item) {
this.setTitle(String('Visitors: '+item.value[1]));
}
}
}]
});
My Store
Ext.define('store.LocationStatisticsStore', {
extend: 'Ext.data.Store',
requires: ['model.LocationStatistics'],
model: 'model.LocationStatistics',
autoLoad: false,
remoteSort: false,
remoteFilter: false,
filterOnLoad: true,
sorters: [{
property: ['YearMonth' , 'Location_Id'],
direction: 'DESC'
}],
proxy: {
type: 'direct',
paramOrder: [],
api: {
read: ServerControllers.Reports.GetLocationStatisticsPerMonth
},
reader: {
type: 'json',
root: 'data',
totalProperty: 'totalRecords',
successProperty: 'success',
messageProperty: 'msg'
}
}
});
My Model:
Ext.define('model.LocationStatistics', {
extend: 'Ext.data.Model',
idProperty: ['Location_Id'],
fields: [
{ name: 'YearMonth', type: 'string' },
{ name: 'Location_Id', type: 'int' },
{ name: 'Year', type: 'int' },
{ name: 'Month', type: 'int' },
{ name: 'TotalCount', type: 'int' },
{ name: 'AverageCountPerDay', type: 'int' }
]
});
Would anyone be so kind as to explain this concept to me, link a tutorial or provide me an example of how to go about doing this?
Help is greatly appreciated.
EDIT: the panel that this is contained in:
Ext.define('view.panel.DashboardStatisticsPanel', {
extend : 'Ext.panel.Panel',
alias : 'widget.DashboardStatisticsPanel',
requires : [
'Ext.layout.container.Column',
'view.chart.LocationStatisticsPerMonth'
],
plain: true,
border: false,
layout: {
type: 'hbox',
padding: '10 10 10 10'
},
items: [{
xtype: 'panel',
layout: 'fit',
border: true,
flex: 1,
items: [{
xtype: 'panel',
layout: 'anchor',
border: true,
overflowX: 'scroll',
height: 400,
dockedItems: [{
xtype: 'toolbar',
dock: 'top',
items: [{
xtype: 'label',
text: 'Select Date',
padding: '0 0 0 10'
},
{
xtype: 'combobox',
displayField: 'YearMonth',
emptyText: 'Select Date ',
queryMode: 'local',
padding: '0 0 0 10',
store: function (btn) {
btn.up('panel').down('LocationStatisticsPerMonthChart').getStore();
},
valueField: 'YearMonth',
}]
}]
/*tbar: [{
text: 'September Only',
handler: function (btn) {
var store = btn.up('panel').down('LocationStatisticsPerMonthChart').getStore();
store.clearFilter();
Ext.defer(function (btn) {
store.filter("Month", 9);
}, 300);
}
}]*/,
items: [{
xtype: 'LocationStatisticsPerMonthChart',
itemId: 'LocationStatisticsPerMonthChart',
anchor: '2000, -10',
//height: 400,
// width: 2000,
}]
}],
dockedItems: [{
xtype: 'TitleToolbar',
dock: 'top',
title: Resources.t('RegistrationsPerUnit'),
items: ['->', {
iconCls: 'iconRefresh',
itemId: 'refresh',
scope: this,
handler: function (btn) {
var store = btn.up('panel').down('LocationStatisticsPerMonthChart').getStore();
//store.removeAll();
store.load();
}
}]
}]
}]
});
The best way is to use a function, I'll give you an example:
We have a store, let's call it StoreA, and another store StoreB
var storeA = something.getStore();
var storeB = somethingElse.getStore();
storeA.filterBy(function(record) {
return storeB.find("name", record.data.name) != -1;
});
So basicly in this example, if the name is in storeB (find returns an index), it will filter it out from storeA's contents.
Unfortunately, you will have to re-do the exact same filter every time a new record is added or updated... so what I do is bind a function that does this to the load, update, delete, etc.. events.
You only need to filter the related data store. To accomplish this use the filter method as follows:
chart.store.filter("Month", 10);
You can always remove the filtering on the store by calling clearFilter store's method,
chart.store.clearFilter();
Take into account that you need to remove previous filters when applying new ones otherwise the each new applied filter will stack with the current ones
You can see a small example based on your code here. Hope it helps you to solve your problem.

Ext.js Category axis vertical labels not centered after rotation

Similar to this post in the Sencha forums, how do I get the labels in this (image below) to show up vertically and line up with the grid? Seems like this should be charting basics, but maybe I missed something.
Here is the jsFiddle with the code: http://jsfiddle.net/wilsjd/kg6Ps/8/
Ext.require('Ext.chart.*');
Ext.require(['Ext.Window', 'Ext.fx.target.Sprite', 'Ext.layout.container.Fit', 'Ext.window.MessageBox']);
Ext.define('CPI', {
extend: 'Ext.data.Model',
fields: [{
name: 'ReportingPeriod',
type: 'string'
}, {
name: 'PeriodAmount',
type: 'decimal'
}, {
name: 'CumulativeAmount',
type: 'decimal'
}]
});
var store1 = Ext.create('Ext.data.Store', {
model: 'CPI',
data: [{
ReportingPeriod: 'Period1',
PeriodAmount: 1,
CumulativeAmount: 1.2,
Target: 1
}, {
ReportingPeriod: 'Period2',
PeriodAmount: 1.2,
CumulativeAmount: .2,
Target: 1
}, {
ReportingPeriod: 'Period9',
PeriodAmount: 1.5,
CumulativeAmount: 0.8,
Target: 1
}]
});
var chart = Ext.create('Ext.chart.Chart', {
style: 'background:#fff',
animate: true,
theme: 'Category1',
store: store1,
width: 300,
height: 300,
renderTo: 'chart',
axes: [{
type: 'Numeric',
position: 'left',
fields: ['PeriodAmount', 'CumulativeAmount'],
title: 'CPI'
},{
type: 'Category',
position: 'bottom',
fields: ['ReportingPeriod'],
title: 'Reporting Period',
label : {
rotation:{degrees:270}
}
}],
series: [{
type: 'column',
axis: 'left',
xField: 'ReportingPeriod',
yField: 'PeriodAmount',
renderer: function(sprite, record, attr, index, store) {
var value = 0;
if(record.get('PeriodAmount')>=1){
value = 0;
}else{
value = 1;
}
var color = ['rgb(127, 255, 127)',
'rgb(255, 0, 50)'
][value];
return Ext.apply(attr, {
fill: color
});
}
}, {
type: 'line',
axis: 'left',
xField: 'ReportingPeriod',
yField: 'CumulativeAmount',
markerConfig: {
type: 'circle',
size: 4,
radius: 4,
'stroke-width': 0,
}
}]
});
chart.show();
I changed rotation to rotate and it works.
label:{
rotate:{degrees:270}
}
Per usual, the people at sencha's forum are as helpful as a punch to the throat: http://www.sencha.com/forum/showthread.php?156746-line-chart-time-axis-label-rotate-issue&p=678586&viewfull=1#post678586
It appears to be set via dy attribute in the html:
<tspan x="96" dy="3.75">Period1</tspan>
Altering dy will move the h-pos left or right. Unfortunately, there doesn't seem to be an inbuilt way to do it though.
Bottom line: it might be a bug (as a dev mentions in the above link -- but helpfully doesn't expand upon).

Ext.js chart series and axis

Suppose I have some data that I want to display on an Ext.js bar chart and I want my data to revolve around a custom value, "1.0" for example, on the y-axis (versus traditionally "0.0").
Meaning, the picture below has negative values represented with inverted bars, I would like values less than "1.0" to show as an inverted bar on the chart.
Is there a setting that I can set to acheive this? If so, would it be in the series and the axis definition? Maybe the chart itself?
The code for the chart displayed is below:
Ext.require('Ext.chart.*');
Ext.require(['Ext.Window', 'Ext.fx.target.Sprite', 'Ext.layout.container.Fit', 'Ext.window.MessageBox']);
Ext.define('CPI', {
extend: 'Ext.data.Model',
fields: [{
name: 'ReportingPeriod',
type: 'string'
}, {
name: 'CPI_P',
type: 'decimal'
}, {
name: 'CPI_C',
type: 'decimal'
}]
});
var store1 = Ext.create('Ext.data.Store', {
model: 'CPI',
data: [{
ReportingPeriod: 'Period1',
CPI_P: '1.9',
CPI_C: '1.2'
}, {
ReportingPeriod: 'Period2',
CPI_P: '1.2',
CPI_C: '1.1'
}, {
ReportingPeriod: 'Period3',
CPI_P: '0.1',
CPI_C: '0.5'
}, {
ReportingPeriod: 'Period4',
CPI_P: '-0.5',
CPI_C: '0.6'
}, {
ReportingPeriod: 'Period5',
CPI_P: '-0.9',
CPI_C: '0.8'
}, {
ReportingPeriod: 'Period6',
CPI_P: '-1.0',
CPI_C: '-0.6'
}, {
ReportingPeriod: 'Period7',
CPI_P: '-1.1',
CPI_C: '-0.7'
}, {
ReportingPeriod: 'Period8',
CPI_P: '-1.5',
CPI_C: '-0.8'
}]
});
var chart = Ext.create('Ext.chart.Chart', {
style: 'background:#fff',
animate: true,
theme: 'Category1',
store: store1,
width: 300,
height: 300,
renderTo: 'chart',
axes: [{
type: 'Numeric',
position: 'left',
fields: ['CPI_P', 'CPI_C'],
title: 'CPI',
grid: true
}, {
type: 'Category',
position: 'bottom',
fields: ['ReportingPeriod'],
title: 'Reporting Period'
}],
series: [{
type: 'column',
axis: 'left',
xField: 'ReportingPeriod',
yField: 'CPI_P',
markerConfig: {
type: 'cross',
size: 3
},
renderer: function(sprite, record, attr, index, store) {
var value = (record.get('CPI_P') >> 2) % 2;
var color = ['rgb(213, 70, 121)',
'rgb(44, 153, 201)'
][value];
return Ext.apply(attr, {
fill: color
});
}
}]
});
chart.show();
UPDATE
If I add a minimum to y-axis I get closer (sort of)
...
axes: [{
type: 'Numeric',
position: 'left',
fields: ['CPI_P', 'CPI_C'],
title: 'CPI',
grid: true,
minimum: 1,
}....
It's the right idea, because the bars now are relevant to the number 1, but I obviously need to keep the bars on the graph.
What has seemed to work is subtracting 1 from my data and adding 1 to the labels.
axes: [{
type: 'Numeric',
position: 'left',
grid: true,
maximum: 1.0, //will render to 2.0
fields: ['PeriodAmount', 'CumulativeAmount'],
label:{
renderer:function(v){
return (v+1).toFixed(2);
}
}
}...]
This is a viable work around for me, but I wouldn't consider this an elegant answer since it isn't very configurable for custom values. For example, changing this to -1 or 2 would require more work than just changing a simple setting.

Resources