I have a stacked bar chart I am trying to make each item in the chart that is clicked open the sharepoint view based on what was clicked.
I am building the data here
seriesData.push({ name: xStatus[i], data: dataArray, url: countArray[i].url });
my chart code looks like this:
loadStackedBarChartWithLink = function (xCategories, seriesData, divId, chartTitle, yAxisTitle) {
//Build Stacked Bar Chart
$(divId).highcharts({
chart: {
type: 'bar'
},
credits: {
enabled: false
},
title: {
text: chartTitle
},
xAxis: {
categories: xCategories,
allowDecimals: false
},
yAxis: {
min: 0,
allowDecimals: false,
title: {
text: yAxisTitle
}
},
legend: {
backgroundColor: '#FFFFFF',
reversed: true
},
plotOptions: {
series: {
cursor: 'pointer',
stacking: 'normal',
point: {
events: {
click: function () {
location.href = this.options.url;
}
}
}
}
},
series: seriesData
});
}
The chart has the correct data, pointer is correct but I get 'undefined' for the url.
Any help is greatly appreciated.
IWSChartBuilder.PersonEngagementsByStatusChart = function () {
var load = function () {
$.when(
IWSChartBuilder.RESTQuery.execute(GET THE DATA HERE)
).done(
function (engagements1) {
var data = [];
var countArray = [];
var urls = [];
var results = engagements1.d.results;
for (var i = 0; i < results.length; i++) {
if (results[i].Consultant.Title == undefined) {
continue;
}
var found = false;
for (var j = 0; j < data.length; j++) {
if (data[j].personName == results[i].Consultant.Title && data[j].statusName == results[i].Engagement_x0020_Status) {
data[j].statusCount = data[j].statusCount + 1;
found = true;
}
}
if (!found) {
data.push(new IWSChartBuilder.StatusByPerson(results[i].Consultant.Title, results[i].Engagement_x0020_Status, 1));
}
}
//Put data into format for stacked bar chart
var seriesData = [];
var xCategories = [];
var xStatus = [];
var i, j, cat, stat;
//Get Categories (Person Name)
for (i = 0; i < data.length; i++) {
cat = data[i].personName;
if (xCategories.indexOf(cat) === -1) {
xCategories[xCategories.length] = cat;
}
}
//Get Status values
for (i = 0; i < data.length; i++) {
stat = data[i].statusName;
if (xStatus.indexOf(stat) === -1) {
xStatus[xStatus.length] = stat;
}
}
//Create initial series data with 0 values
for (i = 0; i < xStatus.length; i++) {
var dataArray = [];
for (j = 0; j < xCategories.length; j++) {
dataArray.push(0);
}
seriesData.push({ name: xStatus[i], data: dataArray });
}
//Cycle through data to assign counts to the proper location in the series data
for (i = 0; i < data.length; i++) {
var personIndex = xCategories.indexOf(data[i].personName);
for (j = 0; j < seriesData.length; j++) {
if(seriesData[j].name == data[i].statusName){
seriesData[j].data[personIndex] = data[i].statusCount;
break;
}
}
}
//Build Chart
IWSChartBuilder.Utilities.loadStackedBarChartWithLink(xCategories, seriesData, "#engagementsByStatusChart", "Engagements by Status", "Total Projects");
}
).fail(
function (engagements1) {
$("#engagementsByStatusChart").html("<strong>An error has occurred.</strong>");
}
);
};
return {
load: load
}
}();
The way I am trying now
plotOptions: {
series: {
cursor: 'pointer',
stacking: 'normal',
point: {
events: {
click: function () {
getURL(this, xCategories, seriesData);
}
}
}
}
},
function getURL(chartInfo, xCategories, seriesData) {
var baseUrl = "correctURL" + xCategories[chartInfo._i] + "-FilterField2%3DEngagement%255Fx0020% 255FStatus-FilterValue2%3D" + chartInfo.name;
location.href = baseUrl;
}
You are hooking into the point click event handler. In this handler, 'this' refers to the point, not the series.
Try this:
plotOptions: {
series: {
cursor: 'pointer',
stacking: 'normal',
events: {
click: function () {
location.href = this.options.url;
}
}
}
},
http://jsfiddle.net/2DG84/
If you need URL's which depend on point values as well as the series, you can access both. e.g.
plotOptions: {
series: {
cursor: 'pointer',
stacking: 'normal',
point: {
events: {
click: function () {
alert(this.series.options.url + "&y=" + this.y);
}
}
}
}
},
In this example, there is a base URL on the series, by I am adding to the UTL with the point.y value.
http://jsfiddle.net/Fygg2/
Related
I've put together a graph which pulls data from google.
I'm trying to change the styling but can't find a way to do it through CSS. I want to add a border to both columns.
I couldunt add all the code, but I can see this is the part that mentions colors.
https://codepen.io/jameswill77/pen/WNzXxeJ
const colors = ['#FDF7F2', '#23F0C7'];
dataj = JSON.parse(data.toJSON());
console.log(dataj.cols[0].label);
const labels = [];
for (c = 1; c < dataj.cols.length; c++) {
if (dataj.cols[c].label != "") {
labels.push(dataj.cols[c].label);
}
}
console.log(labels);
const datasets = [];
for (i = 0; i < dataj.rows.length; i++) {
const series_data = [];
for (j = 1; j < dataj.rows[i].c.length; j++) {
if (dataj.rows[i].c[j] != null) {
if (dataj.rows[i].c[j].v != null) {
series_data.push(dataj.rows[i].c[j].v);
} else {
series_data.push(0);
}
}
}
var dataset = {
label: dataj.rows[i].c[0].v,
backgroundColor: colors[i],
borderColor: colors[i],
data: series_data
}
datasets.push(dataset);
}
console.log(datasets);
const chartdata = {
labels: labels,
datasets: datasets
};
var canvas = document.getElementById("myChart");
var setup = {
type: 'bar',
data: chartdata,
options: {
plugins: {
title: {
display: true,
text: dataj.cols[0].label
}
},
responsive: true,
}
}
chart = new Chart(canvas, setup);
}
added option borderWidth and changed value of borderColor in the datasets.
https://codepen.io/fver1004/pen/MWVOJpR?editors=1010
google.charts.load('current', {
'packages': ['corechart', 'bar']
});
google.charts.setOnLoadCallback(initChart);;
function initChart() {
URL = "https://docs.google.com/spreadsheets/d/1MV4chXniMBHj1ROAIf-BZR3LsoN-HT180pUwivU5Jbc/edit#gid=2112195720";
var query = new google.visualization.Query(URL);
query.setQuery('select *');
query.send(function(response) {
handleQueryResponse(response);
});
}
function handleQueryResponse(response) {
var data = response.getDataTable();
var columns = data.getNumberOfColumns();
var rows = data.getNumberOfRows();
// console.log(data.toJSON());
const colors = ['#eeeeee', '#23F0C7'];
const border_colors = ['#ececec', '#21DEC5'];
dataj = JSON.parse(data.toJSON());
// console.log(dataj.cols[0].label);
const labels = [];
for (c = 1; c < dataj.cols.length; c++) {
if (dataj.cols[c].label != "") {
labels.push(dataj.cols[c].label);
}
}
// console.log(labels);
const datasets = [];
for (i = 0; i < dataj.rows.length; i++) {
const series_data = [];
for (j = 1; j < dataj.rows[i].c.length; j++) {
if (dataj.rows[i].c[j] != null) {
if (dataj.rows[i].c[j].v != null) {
series_data.push(dataj.rows[i].c[j].v);
} else {
series_data.push(0);
}
}
}
var dataset = {
label: dataj.rows[i].c[0].v,
backgroundColor: colors[i],
borderColor: border_colors[i],
borderWidth: 4,
data: series_data
}
datasets.push(dataset);
}
console.log(datasets);
const chartdata = {
labels: labels,
datasets: datasets
};
var canvas = document.getElementById("myChart");
var setup = {
type: 'bar',
data: chartdata,
options: {
plugins: {
title: {
display: true,
text: dataj.cols[0].label
}
},
responsive: true,
}
}
chart = new Chart(canvas, setup);
}
I am trying to use High-charts in reactjs and I am new to both these. In the beginning, everything was fine but now I am unable to solve this error.
The following is my code:
Array.prototype.contains = function (v) {
for (var i = 0; i < this.length; i++) {
if (this[i] === v) return true;
}
return false;
};
Array.prototype.unique = function () {
var arr = [];
for (var i = 0; i < this.length; i++) {
if (!arr.contains(this[i])) {
arr.push(this[i]);
}
}
return arr;
}
export default class Chart extends React.Component {
constructor(props) {
super(props);
this.state = {
installs: [],
isLoaded: false,
}
this.state = {
filteredData: [],
isLoadedFilteredData: false,
showChart: true
}
this.state = {
options: {}
}
}
componentDidMount() {
// setInterval(() => {
fetch('xyz)
.then(res => res.json())
.then(json => {
this.setState({
isLoaded: true,
installs: json,
});
});
}
getFilteredData = async (operatingSystem, applicationServer, database, cloud, serverRelease) => {
fetch(`xyz`)
.then(res => res.json())
.then(json => {
// console.log(json);
this.setState({
isLoadedFilteredData: true,
filteredData: json,
}, console.log("getFilteredData called ... "));
});
}
handleChange = ({ altered, name, text, value }) => {
const nextFormValue = {
[name]: {
text,
value,
altered
}
};
this.setState({ ...this.state, ...nextFormValue }, () =>
console.log("handleChange()", this.state),
);
};
render() {
const { isLoaded, installs } = this.state;
const { isLoadedFilteredData, filteredData } = this.state;
if (!isLoaded) {
return <p>Loading...</p>;
}
else {
var arrayOS = []
var arrayApp = []
var arrayDB = []
var arrayCloud = []
var arrayServerRelease = []
for (var i = 0; i < installs.length; i++) {
arrayOS.push(this.state.installs[i].object.hardwareStackDetails.operatingSystem)
arrayApp.push(this.state.installs[i].object.hardwareStackDetails.applicationServer)
arrayDB.push(this.state.installs[i].object.hardwareStackDetails.database)
arrayCloud.push(this.state.installs[i].object.cloud)
arrayServerRelease.push(this.state.installs[i].object.serverRelease)
}
var operatingSystemTemp = arrayOS.unique();
var applicationServerTemp = arrayApp.unique();
var dataBaseTemp = arrayDB.unique();
var cloudTemp = arrayCloud.unique();
var serverReleaseTemp = arrayServerRelease.unique();
arrayOS = []
arrayApp = []
arrayDB = []
arrayCloud = []
arrayServerRelease = []
for (var i = 0; i < operatingSystemTemp.length; i++) {
arrayOS.push({
text: operatingSystemTemp[i],
value: operatingSystemTemp[i]
})
}
for (var i = 0; i < applicationServerTemp.length; i++) {
arrayApp.push({
text: applicationServerTemp[i],
value: applicationServerTemp[i]
})
}
for (var i = 0; i < dataBaseTemp.length; i++) {
arrayDB.push({
text: dataBaseTemp[i],
value: dataBaseTemp[i],
})
}
for (var i = 0; i < cloudTemp.length; i++) {
arrayCloud.push({
text: cloudTemp[i],
value: cloudTemp[i]
})
}
for (var i = 0; i < serverReleaseTemp.length; i++) {
arrayServerRelease.push({
text: serverReleaseTemp[i],
value: serverReleaseTemp[i]
})
}
if (isLoadedFilteredData){
if(filteredData.length === 0) {
console.log("filteredData")
mapNew = new Map();
labelMap = {};
xlabels = []
seriesDataValue = []
Highcharts.chart('container', {
title: {
text: ''
},
subtitle: {
// text: 'Source: thesolarfoundation.com'
},
xAxis: {
categories: xlabels
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'middle'
},
series: seriesDataValue,
responsive: {
rules: [{
condition: {
maxWidth: 500
},
chartOptions: {
legend: {
layout: 'horizontal',
align: 'center',
verticalAlign: 'bottom'
}
}
}]
}
}
);
}
else {
console.log(filteredData)
var mapNew = new Map();
var labelMap = {};
var xlabels = []
var seriesDataValue = []
for (var j = 0; j < filteredData.length; j++) {
if (filteredData[j].object.status === "PASS") {
var date1 = moment(filteredData[j].object.creationTime);
var date2 = moment(filteredData[j].object.updationTime);
var ms = moment(date2, "DD/MM/YYYY HH:mm:ss").diff(moment(date1, "DD/MM/YYYY HH:mm:ss"));
var d = moment.duration(ms);
// var s = d.format("hh:mm:ss");
console.log(ms)
console.log(d.hours())
var timeInhrs = d.hours()
if (mapNew.has(filteredData[j].object.clientBuild)) {
// timeInhrs = ((timeInhrs + mapNewInnerTemp.get(this.state.kuch[j].object.clientBuild)));
console.log(this.state.filteredData[j].object.clientBuild + " " + timeInhrs);
mapNew.set(this.state.filteredData[j].object.clientBuild, timeInhrs);
} else {
console.log(this.state.filteredData[j].object.clientBuild + " " + timeInhrs);
mapNew.set(this.state.filteredData[j].object.clientBuild, timeInhrs);
labelMap[this.state.filteredData[j].object.clientBuild] = 0; ///
//count = count + 1;
}
}
}
console.log(mapNew)
for (var key in labelMap) {
xlabels.push(key);
}
console.log(xlabels)
var dataValue = [] // array
for (const entry of mapNew.entries()) {
console.log(entry)
for (var key in labelMap) {
if (key === entry[0]) {
dataValue.push(entry[1])
}
}
}
console.log(dataValue)
seriesDataValue.push(
{
data: dataValue,
})
console.log(seriesDataValue)
var elementExists = document.getElementById("container");
if(elementExists === null){
let div = document.createElement("div");
div.id="container";
div.id="select"
document.body.appendChild(div);
}
Highcharts.chart('container',
xAxis: {
categories: xlabels
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'middle'
},
series: seriesDataValue,
responsive: {
rules: [{
condition: {
maxWidth: 500
},
chartOptions: {
legend: {
layout: 'horizontal',
align: 'center',
verticalAlign: 'bottom'
}
}
}]
}
}
);
}
}
return (
<div>
<div id="container"> </div>
</div>
);
}
}
}
The above error gets vanished after the page gets refreshed .
The error results from the fact that you create a chart before a div element with the 'container' id is created.
However, to avoid such problems I recommend you to use officially supported Highcharts wrapper for React: https://www.npmjs.com/package/highcharts-react-official
Using chart.js 2.6 Is there a way to dynamically change the bars in my chart for values above zero and below zero? The graph series data is being generated via a call to a method. Right now its just a random number generator but will be a DB call.
function changeWOWData(chart) {
var datasets = chart.data.datasets;
var labelLen = chart.data.labels.length;
if (datasets[0]) {
for (i = 0, len = datasets.length; i < len; i++) {
try {
for (j = 0, len = labelLen; j < len; j++) {
datasets[i].data[j] = getRandomInt(-100, 100);
}
} catch (e) {
console.log(e.message);
}
}
}
}
Chart looks like this:
I want the chart bars above zero to be blue, the bars below zero to be red.
Any/all replies appreciated. Thanks in advance!
Griff
** Edit ** Added the code from the answer below as such:
var myBarChart = new Chart(wowChart, {
type: 'bar',
data: wowData,
plugins: [{
beforeDraw: function (c) {
var data = c.data.datasets[0].data;
for (var i in data) {
try {
var bar = c.data.datasets[0]._meta[0].data[i]._model;
if (data[i] > 0) {
bar.backgroundColor = '#07C';
} else bar.backgroundColor = '#E82020';
} catch (ex) {
console.log(ex.message);
}
console.log(data[i]);
}
}
}],
options: wowOptions
});
Every other line of the console I see the data element along with the exception
You could accomplish that using the following chart plugin :
plugins: [{
beforeDraw: function(c) {
var data = c.data.datasets[0].data;
for (let i in data) {
let bar = c.data.datasets[0]._meta['0'].data[i]._model;
if (data[i] > 0) {
bar.backgroundColor = '#07C';
} else bar.backgroundColor = '#E82020';
}
}
}]
add this followed by your chart options
ᴅᴇᴍᴏ
var ctx = document.getElementById("canvas").getContext('2d');
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
datasets: [{
label: 'LEGEND',
data: [9, 14, -4, 15, -8, 10]
}]
},
options: {},
plugins: [{
beforeDraw: function(c) {
var data = c.data.datasets[0].data;
for (let i in data) {
let bar = c.data.datasets[0]._meta['0'].data[i]._model;
if (data[i] > 0) {
bar.backgroundColor = '#07C';
} else bar.backgroundColor = '#E82020';
}
}
}]
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script>
<canvas id="canvas" height="180"></canvas>
in chartjs v3 you can use Simply scriptable option
example:
datasets: [
{
data: this.chartData,
backgroundColor(context) {
const index = context.dataIndex
const value = context.dataset.data[index]
return value < 0 ? 'red' : 'blue'
}
}
]
visit https://www.chartjs.org/docs/latest/general/options.html#scriptable-options
Can anyone explain to me how to pass a variable HERE:
series: {regions: [{values:{Variable: '#B8E186',}}]},
Commented parts is what I tried so far. Syntax there works like this {SE:'#000FFF',}
//var myArray = [];
//myArray.push({ 'NO': '#000FFF' });
//var a = [], b = parsedData.Item1, c= '#000FFF';
//a.push('b', 'c');
//var cdata = {
map: 'world_mill',
scaleColors: ['#C8E111', '#007FFF'],
normalizeFunction: 'polynomial',
hoverOpacity: 0.7,
hoverColor: false,
markerStyle: {
initial: {
fill: '#F8E111',
stroke: '#383f47'
}
},
series: {
regions: [{
values:
{
}
}]
},
regionStyle: {
initial: {
fill: '#B8E186'
}
},
backgroundColor: '#FFFFF',
//markers: [
// { latLng: [67.16, 14.24], name: 'Norway' },
//]
//});
//};
//jQuery.each(cdata.mapData.paths, function() {
// var continentCodes = {};
// continentCodes['NO'] = "#128da7";
//cdata.series.regions[0].setValues(continentCodes);
});
//var mapObject = $('#world-map').vectorMap('camel', 'mapObject');
//mapObject.series.regions[0].setValues(parsedData.Item1) = '#000FFF';
//});
});
Assuming that series will have multiple regions, and value of region will have multiple variable values
You will need two for loops
var series = {};
for (i = 0; i < regions.length; i++) {
{
var regions = [];
var values = {};
for (j = 0; j < ValuesInRegion.length; j++) {
{
values[ValuesInRegion[j]['key']] = ValuesInRegion[j]['value'];
}
regions.push(values);
}
series['regions'] = regions;
finally
cdata.series = series;
I'm making an application and I have to display data in a chart. I also only want the last 5 transactions entered to display.
I'm using Backbone.js and Chart.js. In my prototype displaying data was no problem because I just bootstrapped the data. But now that I'm trying to pull the data from my Backbone Collection it's not working. I only get a transparent image
// Model
(function (exports) {
var Transaction = Backbone.Model.extend({
defaults: {
amount: 0,
transactionDate: "",
transactionType: "debit",
category: "miscellaneous",
description: ""
},
categories: [
"salary",
"rent",
"miscellaneous"
],
transactionTypes: [
"credit",
"debit"
],
initialize: function() {
this.set({transactionDate: this.attributes.transactionDate || Date.now()}, {validate: true});
},
validate: function(attrs, options) {
if (attrs['transactionType'] !== undefined &&
!_.contains(this.transactionTypes, attrs['transactionType'].toLowerCase())) {
return 'Invalid type: ' + attrs['transactionType'];
} else if (attrs['category'] !== undefined &&
!_.contains(this.categories, attrs['category'].toLowerCase())) {
return 'Invalid category: ' + attrs['category'];
} else if (attrs['transactionDate'] !== undefined &&
_.isNaN(parseInt(attrs['transactionDate'])) || attrs['transactionDate'] < 0) {
return 'Invalid date: '+ attrs['transactionDate'];
} else if (attrs['amount'] !== undefined &&
_.isNaN(parseInt(attrs['amount'])) || attrs['amount'] < 0) {
return 'Invalid amount: '+ attrs['amount'];
}
return null;
}
});
// export for global use
exports.expensus.Models.Transaction = Transaction;
}(this));
This is the collection I'm using ..
;(function (exports) {
var Transactions = Backbone.Collection.extend({
// stuff and thangs
model: expensus.Models.Transaction,
localStorage: new Backbone.LocalStorage('TransactionsCollection'),
latestFive: function(toJSON) {
this.sortByDate(-1); // sort latest first
if (!toJSON) {
return _.first(this.models, 5);
} else {
var models = _.first(this.models, 5),
idx = -1,
json = [],
model;
while (model = models[++idx]) {
json.push(model.attributes);
}
return json;
}
},
sortByDate: function(dir) {
dir = dir || -1;
this.comparator = function(transaction) {
return dir * transaction.get("transactionDate");
};
this.sort();
},
sortByAmount: function(dir) {
dir = dir || -1;
this.comparator = function(transaction) {
return dir * transaction.get("amount");
};
this.sort();
}
});
exports.expensus.Collections.Transactions = Transactions;
}(this));
And this is the Chart View, I get no errors in dev tools so I'm really at a loss ...
;(function (exports){
var ChartView = Backbone.View.extend({
el: ".home-page",
template: Handlebars.compile($("#chart-template").html()),
chart: null,
initialize: function () {
this.listenTo(this.collection, "add", this.render);
this.listenTo(this.collection, "change", this.render);
this.$(".chart-view-div").html(this.template());
this.chart = new Chart($("#expense-chart")[0].getContext("2d"));
this.render();
},
render: function() {
var self = this;
var data = this.chartData();
self.chart.Doughnut(data, {
responsive: true,
animateScale: true
});
},
chartData: function() {
var collection = this.collection.latestFive(true);
var data = {
vals: [],
labels: [],
allData: []
};
var getData = function(color, highlight, labels, vals, collection) {
var object = {
color: color,
highlight: highlight,
chartData: [
{
value: "",
label: ""
}
]
};
for (var i = 0; i < labels.length; i++ ) {
object.chartData.push(0);
}
for (var j = 0; j < vals.length; j++ ) {
object.chartData.push(0);
}
for (var i = 0; i < collection.length; i++ ) {
var item = collection[i];
var label = labels.indexOf(item.category);
var val = vals.indexOf(item.amount);
object.chartData[ { value: val, label: label } ]++;
}
return object;
};
function getRandomColor() {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++ ) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
for (var i = 0; i < collection.length; i++ ) {
var object = collection[i];
var color = getRandomColor();
var highlight = getRandomColor();
data.labels.push(object.category);
data.vals.push(object.amount);
data.allData.push(getData(color, highlight, data.labels, data.vals, collection));
}
return data;
}
});
exports.expensus.Views.ChartView = ChartView;
}(this));
My Add Transaction View
;(function (exports) {
var AddTransactionView = Backbone.View.extend({
el: "#add-transaction-page",
events: {
"submit .add-transaction-form": "addTransaction"
},
initialize: function() {
this.form = this.$(".add-transaction-form")[0];
},
addTransaction: function(evt) {
if (evt) {
evt.preventDefault();
}
var m = new expensus.Models.Transaction({
transactionDate: Date.now(),
transactionType: this.form["trans-type"].value,
amount: this.form["trans-amount"].value,
description: this.form["trans-description"].value,
category: this.form["trans-category"].value
});
if(m.validationError === null) {
this.collection.add(m);
m.save();
$(this.el).modal("hide");
this.form.reset();
} else {
alert("Model is invalid: " + m.validationError);
}
}
});
exports.expensus.Views.AddTransactionView = AddTransactionView;
}(this));
This is as far as I could get. I've done this before with a different kind of chart but can't for the life of me figure it out with the Doughnut chart.
Thanks, everyone
The main thing i can see is that you pass the chart data which is not in the format that chartjs expects, so it should be an array of objects which have the properties value label and color but you are passing it something different. so a quick fix for that would be to construct an array as described
// Model
var Transaction = Backbone.Model.extend({
defaults: {
amount: 0,
transactionDate: "",
transactionType: "debit",
category: "miscellaneous",
description: ""
},
categories: [
"salary",
"rent",
"miscellaneous"
],
transactionTypes: [
"credit",
"debit"
],
initialize: function() {
this.set({
transactionDate: this.attributes.transactionDate || Date.now()
}, {
validate: true
});
},
validate: function(attrs, options) {
if (attrs['transactionType'] !== undefined && !_.contains(this.transactionTypes, attrs['transactionType'].toLowerCase())) {
return 'Invalid type: ' + attrs['transactionType'];
} else if (attrs['category'] !== undefined && !_.contains(this.categories, attrs['category'].toLowerCase())) {
return 'Invalid category: ' + attrs['category'];
} else if (attrs['transactionDate'] !== undefined && _.isNaN(parseInt(attrs['transactionDate'])) || attrs['transactionDate'] < 0) {
return 'Invalid date: ' + attrs['transactionDate'];
} else if (attrs['amount'] !== undefined && _.isNaN(parseInt(attrs['amount'])) || attrs['amount'] < 0) {
return 'Invalid amount: ' + attrs['amount'];
}
return null;
}
});
var Transactions = Backbone.Collection.extend({
// stuff and thangs
model: Transaction,
latestFive: function(toJSON) {
this.sortByDate(-1); // sort latest first
if (!toJSON) {
return _.first(this.models, 5);
} else {
var models = _.first(this.models, 5),
idx = -1,
json = [],
model;
while (model = models[++idx]) {
json.push(model.attributes);
}
return json;
}
},
sortByDate: function(dir) {
dir = dir || -1;
this.comparator = function(transaction) {
return dir * transaction.get("transactionDate");
};
this.sort();
},
sortByAmount: function(dir) {
dir = dir || -1;
this.comparator = function(transaction) {
return dir * transaction.get("amount");
};
this.sort();
}
});
var ChartView = Backbone.View.extend({
el: ".home-page",
template: Handlebars.compile($("#chart-template").html()),
chart: null,
initialize: function() {
this.listenTo(this.collection, "add", this.render);
this.listenTo(this.collection, "change", this.render);
this.$(".chart-view-div").html(this.template());
this.chart = new Chart(this.$("#expense-chart")[0].getContext("2d"));
this.render();
},
render: function() {
var self = this;
var data = this.chartData();
this.chart.Doughnut(data, {
responsive: true,
animateScale: true
});
},
chartData: function() {
var collection = this.collection.latestFive(true);
var data = [];;
var getData = function(color, highlight, labels, vals, collection) {
var object = {
color: color,
highlight: highlight,
chartData: [{
value: "",
label: ""
}]
};
for (var i = 0; i < labels.length; i++) {
object.chartData.push(0);
}
for (var j = 0; j < vals.length; j++) {
object.chartData.push(0);
}
for (var i = 0; i < collection.length; i++) {
var item = collection[i];
var label = labels.indexOf(item.category);
var val = vals.indexOf(item.amount);
object.chartData[{
value: val,
label: label
}] ++;
}
return object;
};
function getRandomColor() {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
for (var i = 0; i < collection.length; i++) {
var object = collection[i];
var color = getRandomColor();
var highlight = getRandomColor();
data.push({
value: object.amount,
color: color,
label: object.category
});
}
return data;
}
});
var collection = new Transactions([{
amount: 12,
transactionDate: 1417442176000,
transactionType: "debit",
category: "miscellaneous",
description: ""
}, {
amount: 13,
transactionDate: 1417442176000,
transactionType: "credit",
category: "salary",
description: ""
}]);
var view = new ChartView({
collection: collection
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0/handlebars.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.2/backbone.js"></script>
<script src="http://www.chartjs.org/assets/Chart.min.js"></script>
<script id="chart-template" type="text/x-handlebars-template">
<canvas id="expense-chart"></canvas>
</script>
<div class="home-page">
<div class="chart-view-div"></div>
</div>