I am using chart.js v2.5.0.
I want to create charts dynamically without using a global variable.
For example, I want to use code similar to this:
https://jsfiddle.net/DUKEiLL/sf57xw6b/
function UpdateChart(ctrl) {
var config = $("#" + ctrl).data("ChartJs");
config.data.datasets.forEach(function (dataset) {
dataset.data = dataset.data.map(function () {
return randomScalingFactor();
});
});
var ctx = document.getElementById(ctrl).getContext("2d");
var TempMyDoughnut = new Chart(ctx, config);
TempMyDoughnut.update();
}
But it doesn't work properly: when the user presses "update" button and hovers over the chart, previous instance are suddenly displayed.
Since you are creating a new chart on each execution of UpdateChart function, hence you would have to destroy any previous instance of chart to prevent the hover issue.
To accomplish so, you could simply replace your UpdateChart function with the following ...
function UpdateChart(ctrl) {
var config = $("#" + ctrl).data("ChartJs");
config.data.datasets.forEach(function(dataset) {
dataset.data = dataset.data.map(function() {
return randomScalingFactor();
});
});
// destroy previous instance of chart
var meta = config.data.datasets[0]._meta;
for (let i in meta) {
if (meta[i].controller) meta[i].controller.chart.destroy();
}
var ctx = document.getElementById(ctrl).getContext("2d");
var TempMyDoughnut = new Chart(ctx, config);
}
Here is the working example on jsFiddle
Related
I have two canvas the first one is working good, but when I initialize the second one the paper.Tool does not work properly, sometimes the event onMouseMove works others not.
var dataLoad;
var mypapers = []
$(document).ready(function () {
dataLoad = new DataLoad();
mypapers[0] = new paper.PaperScope();
mypapers[1] = new paper.PaperScope();
mypapers[0].setup(document.getElementById('dataCanvas'));
dataLoad.Init();
});
// "returnedData" THIS ARRAY COMES FROM AN AJAX CALL
DataLoad.prototype = {
Init: function () {
var self = this;
var paperData = new
DataReader(document.getElementById('dataCanvas'));
paperData.Init(returnedData[i],mypapers[0]);
paperData.Draw(true);
self.datas.push(paperData);
}
});
Till here everything is good the first canvas is populated with the graphics I setted.
DataReader.prototype = {
Init: function (data,mypaper) {
var self = this;
paper = mypaper;
self.paper = paper;
self.toolPan = new self.paper.Tool()
self.toolPan.activate();
self.toolPan.onMouseDrag = function (event) {
var delta = event.downPoint.subtract(event.point)
self.paper.view.scrollBy(delta)
};
self.toolPan.onMouseMove = function (event) {
self.OnMouseMove(event);
};
self.toolPan.onMouseUp = function (event) {
// AFTER MAKE A SELECTION OF ITEMS IN THE CANVAS CALLING THE SECOND CANVAS
var core = self.HighlightElementsInBox();
self.dc = new DefineComponent();
self.dc.Init(core);
$('#DCCanvas').modal('toggle'); // THE CANVAS IS INSIDE THIS BOOTSTRAP MODAL
}
}
});
/* this initialize the second canvas that basically creates another instance of the same prototype i use to manipulate paperjs in the first canvas */
DefineComponent.prototype = {
Init: function (dataCore) {
var self = this;
mypapers[1].setup(document.getElementById('DCCanvas')); // setting second canvas
var paperDataDC = new DataReader(document.getElementById('DCCanvas'));
paperDataDC.Init(dataCore,mypapers[1]);
paperDataDC.Draw(true);
self.datas.push(paperDatasDC);
}
});
In this second canvas all is drawn correctly, but the events onmousedrag and onmousemove does not works properly the first one move the canvas in another position where the mouse is not and mousemove works only in some places of the canvas not in all.
As you are creating two different paperScopes you will need to toggle between them when working with one or the other.
You are saving both paperScopes inside "mypapers" array
mypapers[0] = new paper.PaperScope();
mypapers[1] = new paper.PaperScope();
So to use any of those you should do
mypapers[0].activate();
// or
mypapers[1].activate();
Check out this (min setup) example of what I mean above.
Also, follow stefan's suggestion if you want more help on this since it's hard for people to try to help without a minimal working example
Just a warning, this is my first ExtJS project.
I have two stores loaded from a webserver successfully.
Store containing positions
Store containing marketData
I've created a third store to hold all of my results.
Now I want to go through each position, find the market data record associated, and run a simple calculation.
I have done this successfully all on the event of clicking a button, but I want to separate out the function of doing the actual calculation... passing in parameters.
For now just to get the concept working I created a function called 'sayHello', but I am getting an error stating... ReferenceError: sayHello is not defined.
Can someone point out what I am doing wrong to create this custom function?
Thanks!
my controller...
Ext.define('ExtApplication1.view.clientdetails.clientdetailsController', {
extend: 'Ext.app.ViewController',
alias: 'controller.clientdetails-clientdetails',
onClickCalculate: function () {
console.log('calculation button was hit');
var targetGrid = Ext.getCmp('positionsGridID');
var positionsStore = targetGrid.store;
var marketDataGrid = Ext.getCmp('marketsGridID');
var marketDataStore = marketDataGrid.store;
var calculatedPositionsDataGrid = Ext.getCmp('calculatedPositionsGridID');
var calculatedPositionsDataStore = calculatedPositionsDataGrid.store;
console.log(calculatedPositionsDataStore);
positionsStore.each(function (record) {
console.log('the details for the whole position');
console.log(record);
var bbSymbol = record.get('BBSymbol');
var singleRecord;
marketDataStore.each(function (record) {
var cycleBBSymbol = record.get('BBSymbol');
if (cycleBBSymbol === bbSymbol){
singleRecord = record;
return false;
}
});
console.log('position I am evaluateing is ' + bbSymbol);
console.log('market data found for ' + singleRecord.get('BBSymbol'));
console.log(singleRecord);
//debugger;
var lastPrice = singleRecord.get('Last_Price');
var settle = singleRecord.get('Px_Settle');
var qty = record.get('Quantity');
var marketName = record.get('Description');
var pnl = (lastPrice - settle) * qty;
console.log(pnl);
calculatedPositionsDataStore.add({
BBSymbol: bbSymbol,
Description: marketName,
Quantity: qty,
CalcPLSett: pnl
});
sayHello(singleRecord);
}, this);
},
sayHello: function (singleRecord) {
alert('hello');
alert(singleRecord);
}
});
You get this error because you're out of the scope of the ViewController.
In
positionsStore.each(function (record) { ...}
You are in the store scope, but the sayHello function is in the ViewController scope.
Assign the ViewController's scope to a variable, should solve your problem:
onClickCalculate: function () {
console.log('calculation button was hit');
var me = this; //NEW LINE
var targetGrid = Ext.getCmp('positionsGridID');
var positionsStore = targetGrid.store;
And then use it in the positionsStore.each function :
me.sayHello(singleRecord)
I just wrote a very simple snippet to understand how jQuery data() functions and the code is as follows:
$(function () {
carousel = function() {
this.prop1 = 1;
this.prop2 = 'two';
this.prop3 = 3;
}
var _str = $('#test'),
$str_data = _str.data();
console.log($str_data);
data = _str.data('carousel');
if (!data) _str.data('carousel' , new carousel());
console.log(data);
if (!data) {
console.log('no data');
}
});
Now, the objective of this code was to add data() to the div element using the new operator and then checking if that piece of data was added, however in my snippet of code in-spite of me adding data to the div element using the below line of code:
if (!data) _str.data('carousel' , new carousel());
When I checked again to see on the next line if data is actually added:
if (!data) {
console.log('no data');
}
The test passes, which means no data was added. So what am I missing?
If there is no data, you are updating the data associated with the element but the value referred by the data variable is not updated that is why it is still giving undefined as its value.
$(function () {
var carousel = function () {
this.prop1 = 1;
this.prop2 = 'two';
this.prop3 = 3;
}
var _str = $('#test'),
$str_data = _str.data();
console.log($str_data);
var data = _str.data('carousel');
if (!data) {
//create a new carousel and assign it to data so that it gets a new value
data = new carousel();
//store the new carousel value
_str.data('carousel', data);
}
console.log(data);
if (!data) {
console.log('no data');
}
});
Demo: Fiddle
The problem is that you are not updating the data variable's value. You need either to set again the data value after setting the carousel or to call directly the jquery function .data() as the example bellow:
data = _str.data('carousel');
// this condition is not updating the variable defined above
if (!data) {
_str.data('carousel' , new carousel());
}
console.log(data);
// you have to update the variable value or to call as
if (!_str.data('carousel')) {
console.log('no data');
}
I have a Web App which uses Google Charts.
There are more than one chart on a page.
I successfully create and render the charts.
Depending on the user's filters, I receive new chart data via Ajax.
How can I reacquire a chart object and update it, if I don't keep the returned object that far in the code?
I wonna do something similar to the following:
function DrawChart()
{
// Code code code ... more code
// Initialize
var chart = new google.visualization.ScatterChart(document.getElementById("my-chart-div"));
// Draw
chart.draw(data, options);
}
And later on:
function UserDidSomething()
{
var newData = MyAjaxCall(...);
var options = ...;
var chart = ...; // What goes here??
chart.draw(newData, options);
}
Thanks in advance,
Shy.
I created a dynamic charts object that keeps the created charts:
/// <summary>
/// This object holds created charts in order to edit them.
/// The key for the chart is the div id (e.g. charts["chart-my-chartname"]).
/// </summary>
var charts = {};
function ChartCreated(divId)
{
return charts[divId] !== undefined && charts[divId] != null;
}
function GetChart(divId)
{
return charts[divId];
}
function AddChart(divId, chart)
{
charts[divId] = chart;
}
function RemoveChart(divId)
{
charts[divId] = null;
}
function CreateOrUpdateChart(divId, chartType, data, options)
{
var chart;
// If the chart was previously created, use its object
if (ChartCreated(divId))
{
chart = GetChart(divId);
}
else // If there was no chart, create and keep it
{
chart = InitializeNewChart(chartType, divId);
AddChart(divId, chart);
}
// Create a new DataTable object using the JavaScript Literal Initializer, and the received JSON data object
data = new google.visualization.DataTable(data);
// Render chart
chart.draw(data, options);
}
function InitializeNewChart(type, divId)
{
var container = document.getElementById(divId);
switch (type)
{
case "Scatter": return new google.visualization.ScatterChart(container);
case "Column": return new google.visualization.ColumnChart(container);
case "Line": return new google.visualization.LineChart(container);
default: return null;
}
}
I understand that in D3, dispatch can be used to fire events to multiple visualisations according to this example.
I also understand that if I want to call a dispatch from an object and pass in the context, I can use apply as shown here.
However, I'm having a hard time combining the arguments from a D3 dispatch and the context that I want.
// create my dispatcher
var probeDispatch = d3.dispatch("probeLoad");
var line_count = 0;
// load a file with a bunch of JSON and send one entry every 50 ms
var lines = [[0,1],[1,2],[2,0]];
var parse_timer = window.setInterval(
function () {
parse_dispatch();
}, 50
);
function parse_dispatch(){
// send two arguments with my dispatch
probeDispatch.probeLoad(lines[line_count][0], lines[line_count][1]);
line_count += 1;
if(line_count >= lines.length){
//line_count = 0
window.clearInterval(parse_timer);
}
}
// my chart object
var genChart = function(label){
this.label = label;
// assume I've drawn my chart somewhere here
probeDispatch.on(("probeLoad."+this.label), this.probeParse);
// this next line isn't working, since the
// console.log in probeLoad still returns undefined
probeDispatch.probeLoad.apply(this);
};
genChart.prototype = {
probeParse: function(probeData, simTime) {
// How do I get the context from the object that's calling probeParse
// into the probeParse scope?
var self = this;
console.log(self.label);
}
};
new genChart("pants");
new genChart("shirt");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
It does set the context properly when you see "pants" in the console.
But then there are 3 undefined's logged, because you also call
// send two arguments with my dispatch
probeDispatch.probeLoad(lines[line_count][0], lines[line_count][1]);
without supplying context.
You need
probeDispatch.probeLoad.apply(instanceOfGenChart, [lines[line_count][0], lines[line_count][1]]);
But enabling that also requires moveing parse_dispatch down the page.
// create my dispatcher
var probeDispatch = d3.dispatch("probeLoad");
var line_count = 0;
// load a file with a bunch of JSON and send one entry every 50 ms
var lines = [[0,1],[1,2],[2,0]];
var parse_timer = window.setInterval(
function () {
parse_dispatch();
}, 50
);
// my chart object
var genChart = function(label){
this.label = label;
// assume I've drawn my chart somewhere here
probeDispatch.on(("probeLoad."+this.label), this.probeParse);
// this next line isn't working, but I don't know what to do
probeDispatch.probeLoad.apply(this);
};
genChart.prototype = {
probeParse: function(probeData, simTime) {
// How do I get the context from the object that's calling probeParse
// into the probeParse scope?
var self = this;
console.log(self.label);
}
};
var instanceOfGenChart = new genChart("pants");
function parse_dispatch(){
// send two arguments with my dispatch
probeDispatch.probeLoad.apply(instanceOfGenChart, [lines[line_count][0], lines[line_count][1]]);
line_count += 1;
if(line_count >= lines.length){
//line_count = 0
window.clearInterval(parse_timer);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
So it turns out to bring the context into the function, I have to bind() it for reasons I'm not too clear on.
// create my dispatcher
var probeDispatch = d3.dispatch("probeLoad");
var line_count = 0;
// load a file with a bunch of JSON and send one entry every 50 ms
var lines = [[0,1],[1,2],[2,0]];
var parse_timer = window.setInterval(
function () {
parse_dispatch();
}, 50
);
function parse_dispatch(){
// send two arguments with my dispatch
probeDispatch.probeLoad(lines[line_count][0], lines[line_count][1]);
line_count += 1;
if(line_count >= lines.length){
//line_count = 0
window.clearInterval(parse_timer);
}
}
// my chart object
var genChart = function(label){
this.label = label;
// assume I've drawn my chart somewhere here
probeDispatch.on(("probeLoad."+this.label), this.probeParse.bind(this));
};
genChart.prototype = {
probeParse: function(probeData, simTime) {
// How do I get the context from the object that's calling probeParse
// into the probeParse scope?
var self = this;
console.log(self.label);
}
};
new genChart("pants");
new genChart("shirt");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Added by meetamit
Bind is the solution here, because it locks a scope to an "instance" of genChart.prototype. probeParse. This way parse_dispatch (the invoker) doesn't need to know anything about scope. It's equivalent to this:
// my chart object
var genChart = function(label){
this.label = label;
var self = this;
var probeParseBound = function() { self.probeParse(); };
probeDispatch.on(("probeLoad."+this.label), probeParseBound);
};