ChartJS rendering old and new data - meteor

I am creating an application using Meteor and ReactJS. I have a collection which I subscribe to and the client side, and I want to render a ChartJS Polar Area chart based on the data from the collection.
The problem is that whenever I update the collection, both the new data and the old data appears on the chart (when I hover, I get two different values for the segments, the old data and the new data).
Here is my code:
ChartComponent = React.createClass({
mixins: [ReactMeteorData],
getMeteordata() {
let data = Meteor.subscribe("sentiment");
return {
chartData: [
{
value: MyCollection.find({"item": "test1"}).count(),
color: "green",
label: "Test1"
},
{
value: MyCollection.find({"item": "test2"}).count(),
color: "yellow",
label: "Test2"
},
{
value: MyCollection.find({"item": "test3"}).count(),
color: "red",
label: "Test3"
}
]
}
},
_drawGraph() {
let ctx = document.getElementById("my-canvas").getContext("2d");
let polarChart = new Chart(ctx).Polararea(this.data.chartData, {
animateScale: true,
});
return polarChart;
},
componentDidMount() {
this._drawGraph();
},
componentDidUpdate() {
this._drawGraph().update();
},
render() {
return <canvas id="my-canvas"></canvas>
}
});
Help would be appreciated. Thank you very much!
Note:
I am using react version 0.14. The official react-chartjs component does not work, due to some of the api changes.

Related

Vuefire with Chart on Vue.js

I am trying to sync my chart' array data with my firebase value. Chart getting arrays for label and data. I would like to bind this Chart data value to my firebase object. Here is normal chart creation with hard coded static values:
data()
{
return {appChart: {data: {labels: ["A"], series: [[3]]}},}
},
Now, I would like to bind this value (3) with my firebase object.. Normally I can use firebase object like this with no problem:
new Vue({
data: () => ({ myObjA: null }),
firebase: {
myObjA: db.ref('myObjA'),
asObject:true
},
})
But I cant bind and dynamic update this myObjA with Chart array series ‘a’ like;
data()
{
return {appChart: {data: {labels: ["A"], series: [[myObjA]]}},}
},
How can I do that ?
You can change appChart to be computed properties:
export default {
data: () => ({ myObjA: null }),
firebase: {
myObjA: db.ref('myObjA'),
asObject:true
},
computed: {
appChart() {
return {
data: {
labels: ["A"],
series: [[this.myObjA]]
}
}
}
}
}
</script>

Cytoscape.js cy.style().fromJson() add instead of replace?

I want to separate the style from color schemes in a Cytoscape.js 3.1 graph. According to http://js.cytoscape.org/#cy.style, I initialize and then add to cy.style():
var mystyle = [
{
"selector": "node",
"css":
{
"border-opacity": 1.0,
'label': function(ele)
{
if(ele.data('Labels_EN')) {return ele.data('Labels_EN')[0];}
...
}, ...
];
var bright = [
{
"selector": "node",
"css":
{"background-color": "white";}
}];
cy = cytoscape(
{
container: mycontainer,
style: mystyle
});
cy.style().fromJson(bright).update();
Unfortunately, the call to cy.style().fromJson() seems to automatically invoke cy.style().resetToDefault(), as it removes the existing style.
How can I prevent Cytoscape.js from deleting my existing style and instead add to it using JSON? I need this functionality so that I don't have to put the complete style information in all my color scheme files, which makes it harder to maintain.
P.S.: As a workaround, I merged two style files like this:
function mergeJsonArraysByKey(a1,a2)
{
let map1 = new Map();
let map2 = new Map();
for(i=0;i<a1.length;i++) {if(a1[i].selector) {map1.set(a1[i].selector,a1[i]);}}
for(i=0;i<a2.length;i++) {if(a2[i].selector) {map2.set(a2[i].selector,a2[i]);}}
let merged = [];
map1.forEach((value,key,map) =>
{
if(map2.has(key))
{
merged.push($.extend(true,{},value,map2.get(key)));
} else
{
merged.push(value);
}
});
map2.forEach((value,key,map) =>
{
if(!map1.has(key))
{
merged.push(value);
}
});
return merged;
}
function initGraph(container, graph)
{
let merged = mergeJsonArraysByKey(mystyle,bright);
cy = cytoscape(
{
container: container,
style: merged
});
}
Merge the json rather than appending to the stylesheet. You can do whatever you want with the json, whereas cy.style() can be modified in only particular ways to prevent issues.
cy.style().fromJson( sheetA.concat( sheetB ) )

How can we disable the datatable export buttons(Or prevent click event) when datatable is empty?

I'm using datatable , I need to disable the export buttons(Excel, PDF) when the grid having the empty rows.
How can we handle the datatable export buttons click actions?
I have done the grid initialization as below.
I don't know how to handle the datatable export buttons(PDF, Excel).
Can you please help me how to fix this.
Update:
And I have one more issue that when user click on the Excel or PDF button then the grid columns width is collapsing.
I need even user click on the Excel or PDF buttons the grid column width should not change. How can we achieve this?
After exporting the data in to excel, columns are not auto adjusted in the excel.
How can we make the columns(After export datatable data into excel) auto adjusted?
var buttonCommon = {
exportOptions: {
format: {
body: function(data, column, row, node) {
return data;
}
}
}
};
var dataTableObj = {
"processing": true,
"destroy": true,
"scrollX": true,
"columns": [{
"data": "CollegeName",
"width":"30%"
}, {
"data": "AffiliatedTo",
"width":"15%"
}, {
"data": "TPOName",
"width":"20%"
}, {
"data": "Phone",
}, {
"data": "Website",
"bSortable": false
}],
dom: 'lBfrtip',
buttons: [
$.extend( true, {}, buttonCommon, {
extend: 'excelHtml5',
title: 'Colleges',
} ),
{
extend: 'pdf',
title: 'Colleges'
}
],
fnRowCallback: function(nRow, aData, iDisplayIndex) {
//Some code
return nRow;
}
};
var dataTbl = $('#tblColleges').DataTable(dataTableObj);
I don't know how far following solution makes sense but it was resolved my issue.
I need to disable the export buttons(Excel, PDF) when the grid having
the empty rows. How can we handle the datatable export buttons click
actions?
here I have done with the datatables action property.
buttons: [{
extend: 'excelHtml5',
title: 'Colleges',
action: function(e, dt, button, config) {
if (this.data().length > 0) {
$.fn.dataTable.ext.buttons.excelHtml5.action(e, dt, button, config);
$scope.hasAlert = 0;
$scope.$apply();
} else {
$scope.hasAlert = 2;
$scope.alertMsg = __APP_MESSAGE__.GridEmptyMsg;
$scope.$apply();
}
}
}, {
extend: 'pdf',
title: 'Colleges',
action: function(e, dt, button, config) {
if (this.data().length > 0) {
$.fn.dataTable.ext.buttons.pdfHtml5.action(e, dt, button, config);
$scope.hasAlert = 0;
$scope.$apply();
} else {
$scope.hasAlert = 2;
$scope.alertMsg = __APP_MESSAGE__.GridEmptyMsg;
$scope.$apply();
}
}
},
]
This solution has worked for me put it in fnDrawCallback function
$(tableName).dataTable({
"fnDrawCallback":function () {
var table = $(tableName).DataTable();
if (table.data().length === 0)
table.buttons('.buttons-html5').disable();
else
table.buttons('.buttons-html5').enable();
}
});
The easiest way to handle the datatable export buttons click actions is to create your own button and trigger the datatable export button on the click of your button.
This way you will handle the click actions of your created button:
$('#exportBtn').on('click', function () {
dataTbl.button(0).trigger();
});
You can remove dom: 'lBfrtip' to hide the original datatable button.

How do I get translated column headers with Meteor and aldeed:tabular?

I'm running into the same problem as issue #53 of aldeed:tabular. When defining the table as suggested in the documentation, it is too soon to invoke a translation function (TAPi18n.__ or other), since the I18N variables are not yet set.
What is the nice, reactive way of feeding the translated column titles into DataTables, either directly as suggested by aldeed himself upon closing the issue, or through aldeed:tabular?
With .tabular.options
There is a way with the template's .tabular.options reactive
variable, but it is quirky. Here is a variation of the library
example using
tap-i18n to translate the
column headers:
function __(key) {
if (Meteor.isServer) {
return key;
} else {
return TAPi18n.__(key);
}
}
Books = new Meteor.Collection("Books");
TabularTables = {};
TabularTables.Books = new Tabular.Table({
name: "Books",
collection: Books,
columns: [] // Initially empty, reactively updated below
});
var getTranslatedColumns = function() {
return [
{data: "title", title: __("Title")},
{data: "author", title: __("Author")},
{data: "copies", title: __("Copies Available")},
{
data: "lastCheckedOut",
title: __("Last Checkout"),
render: function (val, type, doc) {
if (val instanceof Date) {
return moment(val).calendar();
} else {
return "Never";
}
}
},
{data: "summary", title: __("Summary")},
{
tmpl: Meteor.isClient && Template.bookCheckOutCell
}
];
}
if (Meteor.isClient) {
Template.tabular.onRendered(function() {
var self = this;
self.autorun(function() {
var options = _.clone(self.tabular.options.get());
options.columns = getTranslatedColumns();
self.tabular.options.set(_.clone(options));
});
});
}
With a forked version
I created a pull request against branch devel of meteor-tabular to enable the straightforward, reactive-based approach like so:
<template name="MyTemplateWithATable">
{{> tabular table=makeTable class="table table-editable table-striped table-bordered table-condensed"}}
</template>
var MyColumns = ["title", "author"];
// Assume translations are set up for "MyTable.column.title", "MyTable.column.author"
// in other source files; see TAPi18n documentation for how to do that
function makeTable() {
return new Tabular.Table({
name: "MyTable",
collection: MyCollection,
columns: _.map(MyColumns,
function(colSymbol) {
return {
data: colSymbol,
title: TAPi18n.__("MyTable.column." + colSymbol)
};
})
});
}
if (Meteor.isServer) {
// Called only once
makeTable();
} else if (Meteor.isClient) {
// Reactively called multiple times e.g. when switching languages
Template.MyTemplateWithATable.helpers({makeTable: makeTable});
}
Recent versions of aldeed:tabular allow to specify a function for setting the column titles.
import {TAPi18n} from 'meteor/tap:i18n';
TabularTables = {};
TabularTables.Departments= new Tabular.Table({
name: 'Departments',
collection: Departments,
responsive: true,
autoWidth: true,
stateSave: false,
columns: [
{data: "name", titleFn: function() {
return TAPi18n.__("name");
}},
{data: "description", titleFn: function() {
return TAPi18n.__("description");
}}
]
});
The language change is reactive. If you have translations you can switch and columns will be translated.
TAPi18n.setLanguage("en");
TAPi18n.setLanguage("de");
Word of warning:
This currently does not work when you include invisible columns in your table data. The offset is wrong and you get wrong column titles.

How to get Clicked series name when clicking on dojox.charting.Chart?

i'm trying to create a chart using dojo. I choose a StackedColumns chart. I want to make it interactive. When a user clicks on a graph columns, an hyperlink should be invoked. But to create hyperlink'url, I need the series name. Is there any way to get Series Name when user clicks on a column? I've been searching for days but i didn't found any solution. Here is my code:
<div id="chartNode" style="width: 1024px; height: 768px;"></div>
<div id="legend"></div>
<script type="text/javascript">
require(['dojox/charting/Chart',
'dojox/charting/themes/PrimaryColors',
'dojox/charting/plot2d/StackedColumns',
'dojox/charting/plot2d/Grid',
'dojox/charting/widget/Legend',
'dojox/charting/action2d/Tooltip',
'dojox/charting/action2d/Magnify',
'dojox/charting/action2d/Highlight',
'dojo/store/Observable',
'dojo/store/Memory',
'dojox/charting/StoreSeries',
'dojox/charting/axis2d/Default',
'dojo/domReady!'], function(Chart, theme, StackedColumns,Grid,Legend,Tooltip,Magnify,Highlight,Observable,Memory,StoreSeries){
var myData = [{id:1,value:1,site:'1'},
{id:2,value:2,site:'1'},
{id:3,value:6,site:'1'},
{id:4,value:4,site:'1'},
{id:5,value:5,site:'1'},
{id:6,value:1,site:'2'},
{id:7,value:3,site:'2'},
{id:8,value:1,site:'2'},
{id:9,value:2,site:'2'},
{id:10,value:7,site:'2'}];
var myStore = new Observable(new Memory({
data: { identifier: 'id',
items: myData
}
}));
var serie_1 = new StoreSeries(myStore, { query: { site: 1 } }, 'value');
var serie_2 = new StoreSeries(myStore, { query: { site: 2 } }, 'value');
var myChart = new Chart('chartNode');
myChart.setTheme(theme);
myChart.addAxis('x', { fixLower: 'minor',
fixUpper: 'minor',
natural: true,
rotation: 90
});
myChart.addAxis('y', {vertical: true,fixLower: 'major',fixUpper: 'major',minorTicks: true,includeZero: true});
myChart.addPlot('myPlot', { type: 'StackedColumns', gap: 5, minBarSize: 8});
myChart.addSeries('Serie 1',serie_1,{ stroke: { color: 'red' }, fill: 'lightpink' });
myChart.addSeries('Serie 2',serie_2,{ stroke: { color: 'blue' }, fill: 'lightblue' });
var highlight = new Highlight(myChart, 'myPlot');
myChart.render();
var legend = new Legend({ chart: myChart }, 'legend');
myChart.connectToPlot('myPlot',function(evt) {
// React to click event
if(type == 'onclick') {
var msg = 'x:'+evt.x+';y:'+evt.y+';index: '+evt.index+';value: '+evt.run.data[evt.index];
alert(msg);
//How to get series informations when onclick event fires???
}
var tip = new Tooltip(myChart,'myPlot',{text:function(evt){return evt.run.data[evt.index];}});
});
});
</script>
I tried this in a stacked bar and works well:
chart.connectToPlot("default", function(evt) {var type = evt.type; if(type=="onclick") console.log(evt.run.name);})
evt.run.name provides the series name based on the column you clicked
Great! Thank you Noelia!
I have used it for a pie chart. You can get the index number of the slice with the parameter evt.index.

Resources