When is "is_initial" true in Dygraph's drawCallback? - javascript

I have a Dygraphs chart that works perfectly when I provide a file URL for a data source. When I embedded the data directly into the HTML wrapper, however, the functions in my drawCallback don't fire. Tracing with Firebug, I find that is_initial is True when I load the page with the URL reference, but False when I embed the data (and labels) in native format, even if I place onLoad="drawCallback(vGraph,True);" within the <body> tag. I've "solved" this by setting my own variable to test for first-time execution.
Here's the original, functional, code for an external data source:
var vGraph = new Dygraph(document.getElementById("dgraphChartContainer"),
ExternalDataSource.csv,
{ //options
connectSeparatedPoints: true,
labelsDiv: "dygraphLabelsContainer",
...
}
);
vGraph.updateOptions({
highlightCallback: function(event, xdate, points_array, rowNumber, seriesName) {
...
},
unhighlightCallback: function(event) {
...
},
drawCallback: function(g, is_initial) {
if (!is_initial) return;
buildTagList(vGraph.getLabels());
mySeriesColors = vGraph.getColors();
buildStyleDefinitions();
}
});
As I said, this works great, even with the blind g parameter in the drawCallback.
This is the work-around I developed for the scenario when I embed the data source.
var vFirstTime = true;
var vGraph = new Dygraph(document.getElementById("dgraphChartContainer"),
[
[ new Date("2011/10/15 00:04:55"),null,null,-9.2,null,null,null,null,null,null],
[ new Date("2011/10/24 10:39:32"),null,null,null,null,null,-9.2,null,null,null],
...
[ new Date("2011/10/25 21:02:30"),null,null,null,null,null,null,null,20.3,null],
[ new Date("2013/10/28 08:49:52"),null,null,-17.9,null,null,null,null,null,null]
],
{ //options
labels: ["Event_Date","code-32565","code-32566","code-32568","code-32569","code-32573","code-32574","code-32575","code-32577","code-32578"],
connectSeparatedPoints: true,
labelsDiv: "dygraphLabelsContainer",
...
}
);
vGraph.updateOptions({
highlightCallback: function(event, xdate, points_array, rowNumber, seriesName) {
...
},
unhighlightCallback: function(event) {
...
},
// drawCallback: function(g, is_initial) {
// if (!is_initial) return;
drawCallback: function() {
if (!vFirstTime) return;
buildTagList(vGraph.getLabels());
mySeriesColors = vGraph.getColors();
buildStyleDefinitions();
vFirstTime=false;
}
});
Is there something I can do to use is_initial in my drawCallback call regardless of the data source?

Your first example will work if you move your callbacks into the constructor:
var vGraph = new Dygraph(document.getElementById("dgraphChartContainer"),
ExternalDataSource.csv,
{ //options
connectSeparatedPoints: true,
labelsDiv: "dygraphLabelsContainer",
highlightCallback: function(event, xdate, points_array, rowNumber, seriesName) {
...
},
unhighlightCallback: function(event) {
...
},
drawCallback: function(g, is_initial) {
if (!is_initial) return;
buildTagList(vGraph.getLabels());
mySeriesColors = vGraph.getColors();
buildStyleDefinitions();
},
...
});
So, what's going on here?
The drawCallback gets fired with is_initial = true when the chart draws for the first time. In your original code, this happens after the XMLHttpRequest for the data comes back.
The order of operations is:
constructor
updateOptions
drawCallback(is_initial=true)
When you inline your data, dygraphs doesn't need to wait for the XHR to come back. Now, the order of operations is:
constructor
drawCallback(is_initial=true)
updateOptions
drawCallback(is_initial=false)
The second drawCallback happens because you called updateOptions(). So drawCallback is getting fired with is_initial = true, it's just that you're not listening for it early enough.
dygraphs provides a .ready() method to let you avoid all this intricacy. You may be happier using it instead:
var vGraph = new Dygraph( ... );
vGraph.ready(function() {
buildTagList(vGraph.getLabels());
mySeriesColors = vGraph.getColors();
buildStyleDefinitions();
});

Related

Accessing a previously drawn plot in javascript/jquery

I draw a plot like this:
var items = $.get("./moonlight_sonata_diameter.data", function(data) {
items = data.split(/\r?\n/).map( pair => pair.split(/\s+/).map(Number) );
$(function () {
plot = $.plot($("#placeholder"),
[ { data: linePoints} ], {
series: {
lines: { show: true }
},
crosshair: { mode: "x" },
grid: { hoverable: true, autoHighlight: false },
yaxis: { min: 0, max: 5 }
});
});
});
Now at a later moment in time, I want to update the crosshair of the plot. However, because it is embedded in so many functions, I don't know how to access it as I am not familiar with jQuery.
Within the script, I can run:
plot.setCrosshair({x: 100})
However, in another script, at another time, there is no object called plot. Is there a way to access it still?
Actually you have put your plot creation code in document ready function and your
plot.setCrosshair({x:100}) is executed just before your plot creation code. so A simple settimeout will do the trick.
just replace
plot.setCrosshair({x: 4})
with
setTimeout(function(){ plot.setCrosshair({x: 41})}, 3000);
and this will work fine. if you call your setCrosshair function after loading the complete dom then you will not need of setTimeout function. I hope this will help and if not then let me know.
Check it at http://plnkr.co/edit/3cMHmzWEIk6c39mblb0Z?p=preview

Not all attributes of JavaScript object being mapped into JSON

I'm using Inkfilepicker.io for picture uploading on a project I'm working on. When you upload a picture, it returns a JavaScript object with some data in it. I need more, though. So I query their API using their SDK to get more. I then add this information to each object. Lastly, I post these objects using jQuery to process it on the backend. However, none of the second batch of information goes along for the ride on the backend. I manually used JSON.stringify() to see what the data looked like before it was posted. The new data was not included.
fp.pickAndStore({multiple: true}, {}, function (InkBlobs)
{
InkBlobs = $.map(InkBlobs, function(blobio, index)
{
fp.stat(blobio, {
width: true,
height: true
}, function(meta) {
blobio.width = meta.width;
blobio.height = meta.height;
});
return blobio;
});
console.log(InkBlobs);
console.log(JSON.stringify(InkBlobs));
$.post('/picture', { blobs: InkBlobs }, function(data)
{
console.log(data);
}, 'json');
});
How can I make sure all of the attributes get sent?
UPDATE
The string created by the JSON.stringify() looks like the following:
[{"url":"https://www.filepicker.io/api/file/PinzEEUlRhSCpc4dYa0w","filename":"browserWars.jpeg","mimetype":"image/jpeg","size":34284,"key":"Vx1xEuqHTKKCM4hjR6LL_browserWars.jpeg","container":"kandidlypictures","isWriteable":true},{"url":"https://www.filepicker.io/api/file/wdnWcCUWStiBxbrONeSN","filename":"1440-music-is-the-food.jpg","mimetype":"image/jpeg","size":97814,"key":"0RrSFSQBTCiifZ8ZkuIj_1440-music-is-the-food.jpg","container":"kandidlypictures","isWriteable":true},{"url":"https://www.filepicker.io/api/file/trdKhlORPCJEu3JRbPYf","filename":"any_browser.jpeg","mimetype":"image/jpeg","size":271194,"key":"1aJNE9MSEyEiAJZAacfD_any_browser.jpeg","container":"kandidlypictures","isWriteable":true}]
The expected output should also include a width and height for each object.
Use a separate named function:
var data = {
width: true,
height: true
};
var fn = function fn(meta)
{
blobio.width = meta.width;
blobio.height = meta.height;
$.post('/picture', { blobs: InkBlobs }, function(data)
{
console.log(data);
}, 'json');
};
fp.statSync(blobio, data, fn)
Or use statSync since stat is asynchronous:
fp.statSync(blobio, {
width: true,
height: true
}, function(meta) {
blobio.width = meta.width;
blobio.height = meta.height;
});
References
Fun with Named Functions
statSync

ng-grid: Service Called Twice On Initial Rendering

Using ng-grid with server side sorting and paging. It works great, with one caveat: the initial rendering makes two calls to get data from my service.
I'm not sure how easy (or hard) this would be to replicate in a jsFiddle or plunker.
Here is my controller code:
function reportQueueController($scope, $location, reportDataService) {
function init() {
$scope.state = {};
}
$scope.setPagingData = function (data) {
$scope.reportQueueList = data.Data;
$scope.totalServerItems = data.TotalItems;
};
$scope.$watch('pagingOptions', function(newVal, oldVal) {
if (newVal === oldVal) return;
getPagedDataAsync();
}, true);
$scope.pagingOptions = {
pageSizes: [25, 50, 100, 'All'],
pageSize: 25,
currentPage: 1
};
$scope.$watch('gridOptions.ngGrid.config.sortInfo', function (newVal, oldVal) {
if (newVal === oldVal) return;
$scope.state.sortField = newVal.fields[0];
$scope.state.sortDirection = newVal.directions[0];
$scope.pagingOptions.currentPage = 1;
getPagedDataAsync();
}, true);
$scope.gridOptions = {
data: 'reportQueueList',
enablePaging: true,
enableRowSelection: false,
showFooter: true,
pagingOptions: $scope.pagingOptions,
totalServerItems: 'totalServerItems',
enableSorting: true,
useExternalSorting: true,
sortInfo: { fields: ['CustomerName'], directions: ['asc'] },
filterOptions: $scope.filterOptions,
columnDefs: [
{ field: 'CustomerName', displayName: 'Customer' },
{ field: 'ParentCustomerName', displayName: 'Parent' },
{ field: 'Name', displayName: 'Report Name' },
{ field: 'Emails', displayName: 'Email Recipients', cellTemplate: emailCellTemplate },
{ cellTemplate: editCellTemplate, width: '50px' }
]
};
function getPagedDataAsync() {
console.log('in get data'); //this get logged twice
reportDataService.getReportQueueList($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage, $scope.state.emailAddress, $scope.state.reportSearch, $scope.state.sortField, $scope.state.sortDirection).then(function(data) {
$scope.setPagingData(data);
});
};
init();
}
Since Angular is going to call your watch at least twice, maybe more due to dirty processing, per $digest cycle you could use debounce. This is similar to what windows event listeners sometimes do. underscore (http://underscorejs.org) and lo-dash (http://lodash.com) both offer a _.debounce() you can use right out of the box.
_.debounce() allows you to say that a function should run, at most, once every per the specified number of milliseconds- no matter how many times the function is actually called. So you might do something like:
var checkSortData = _.debounce(function(e) {
$scope.state.sortField = newVal.fields[0];
$scope.state.sortDirection = newVal.directions[0];
$scope.pagingOptions.currentPage = 1;
getPagedDataAsync();
}, 500); // Run no more than once every 500 milliseconds
As you'd imagine underscore uses $timeout to do this, so you could write your own debounce if you preferred.
Using debounce could help with performance/server load too by minimizing server calls.
But rather than paying the performance price of polling the server to see if it has updated you might also consider using something like http://socket.io. Then you wouldn't have to poll using a watch, you can just attach an event listener on the client side. Here's an article on using socket.io with Angular written by Brian Ford: http://www.html5rocks.com/en/tutorials/frameworks/angular-websockets/
Your code looks correct, check if you are using unobtrusive js file twice, like
jquery.validate.unobtrusive.js
jquery.validate.unobtrusive.min.js
Or the same file is adding twice.

Variable in javascript file not accessible in object property

I have the following problem. I have a JS-file which has a handful of variables. Those I initialize in a function:
var currentYear;
var previousYear;
var urlQuarterDates;
var urlHalfYear;
var urlYear;
var urlMonth;
var urlProposalsSentAndReceived; //= '/Marketing/ListProposalsSentAndReceived';
var urlProposalsResponsibleMonth;
function initTabReportProposalsMonth(_currentYear, _previousYear, _urlViewProposal,
_urlQuarterDates, _urlHalfYear, _urlYear, _urlMonth, _urlProposalsSentAndReceived,
_urlProposalsResponsibleMonth) {
currentYear = _currentYear;
previousYear = _previousYear;
urlQuarterDates = _urlQuarterDates;
urlHalfYear = _urlHalfYear;
urlYear = _urlYear;
urlMonth = _urlMonth;
urlProposalsSentAndReceived = _urlProposalsSentAndReceived;
urlProposalsResponsibleMonth = _urlProposalsResponsibleMonth;
}
I have defined an event handler in the same JS-file:
function onPeriodSelect(combo, rec, i) {
var conn = new Ext.data.Connection();
var params = { };
switch(rec.get('myId'))
{
case _currentQuarter1:
conn.url = urlQuarterDates;
params.y = currentYear;
params.index = 1;
break;
}
reload(); //
}
The variables urlQuarterDates and currentYear are readily accessible. So far, so good...
I also have an ExtJs Grid with a data store which is declared inline:
var gridSentAndReceived = new Ext.grid.GridPanel({
title: 'Totaal',
autoHeight: true,
autoWidth: true,
store: new Ext.data.Store({
id: 'idStoreSentAndReceived',
proxy: new Ext.data.HttpProxy({ url: urlProposalsSentAndReceived,
timeout: 1800000 }),
reader: new Ext.data.JsonReader(
{
root: 'rows'
},
[
{ name: 'Status' },
{ name: 'nrOfProposals' },
{ name: 'TotalRevenueHardware' },
{ name: 'TotalRevenueYearly' },
{ name: 'TotalRevenueHours' }
]),
remoteSort: false
}),
frame: true,
iconCls: 'icon-grid',
columns: [
...
],
viewConfig: {
forceFit: true
}
});
The reload() function calls the load of the store of gridSentAndReceived. This generates an exception: the url is not defined at all. If I initialize the url right at its declaration (which is currently commented out' it works fine. When I browse using the debugger it shows that urlProposalsSentAndReceived is initialized. Still, it claims there is no URL.
This seems to be a scope problem, since variables are accessible from the event handler but obviously not elsewhere. Anybody knows how to fix it? The URLs are created using server tags and those cannot be put in JS files. I wouldn't enjoy putting them directly in the JS file as a text string. Is there a possible solution?
Update
I have tried a few more things but nothing works.
I have tried:
'beforeload': function (store, options) {
store.proxy.setUrl('/Marketing/ListProposalsSentAndReceived');
}
but even that didn't work. Still got the same exception. I really have no clue why that failed though, I took the code from the ExtJs Documentation under 'api'.
Now I have no choice but hardcoding the urls in my js-file though I'd very much prefer to use servertags and add them dynamically. Hopefully, one day, I'll find a solution rather than getting runtime errors when I change the location of a controller action.
This is not a scope issue. At the time you run your code urlProposalsSentAndReceived is not defined. If you set that variable via an event handler, the value is always set after gridSentAndReceived is initialized.

Using cellUpdateEvent with YUI DataTable and JSON DataSource

I'm working with a UI that has a (YUI2) JSON DataSource that's being used to populate a DataTable. What I would like to do is, when a value in the table gets updated, perform a simple animation on the cell whose value changed.
Here are some relevant snippets of code:
var columns = [
{key: 'foo'},
{key: 'bar'},
{key: 'baz'}
];
var dataSource = new YAHOO.util.DataSource('/someUrl');
dataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
dataSource.connXhrMode = 'queueRequests';
dataSource.responseSchema = {
resultsList: 'results',
fields: [
{key: 'foo'},
{key: 'bar'},
{key: 'baz'}
]
};
var dataTable = new YAHOO.widget.DataTable('container', columns, dataSource);
var callback = function() {
success: dataTable.onDataReturnReplaceRows,
failure: function() {
// error handling code
},
scope: dataTable
};
dataSource.setInterval(1000, null, callback);
And here's what I'd like to do with it:
dataTable.subscribe('cellUpdateEvent', function(record, column, oldData) {
var td = dataTable.getTdEl({record: record, column: column});
YAHOO.util.Dom.setStyle(td, 'backgroundColor', '#ffff00');
var animation = new YAHOO.util.ColorAnim(td, {
backgroundColor: {
to: '#ffffff';
}
});
animation.animate();
};
However, it doesn't seem like using cellUpdateEvent works. Does a cell that's updated as a result of the setInterval callback even fire a cellUpdateEvent?
It may be that I don't fully understand what's going on under the hood with DataTable. Perhaps the whole table is being redrawn every time the data is queried, so it doesn't know or care about changes to individual cells?. Is the solution to write my own specific function to replace onDataReturnReplaceRows? Could someone enlighten me on how I might go about accomplishing this?
Edit:
After digging through datatable-debug.js, it looks like onDataReturnReplaceRows won't fire the cellUpdateEvent. It calls reset() on the RecordSet that's backing the DataTable, which deletes all of the rows; it then re-populates the table with fresh data. I tried changing it to use onDataReturnUpdateRows, but that doesn't seem to work either.
Edit2:
To achieve the control that I wanted, I ended up writing my own <ul>-based data list that made a bit more sense for the problem I was trying to solve. Jenny's answer below should help solve this for most others, so I've accepted it as the solution.
cellUpdateEvent only fires in response to a call to updateCell(). What you want is to subscribe to the cellFormatEvent. There were a couple other issues in your code, so this should work:
dataTable.subscribe('cellFormatEvent', function(o) {
YAHOO.util.Dom.setStyle(o.el, 'backgroundColor', '#ffff00');
var animation = new YAHOO.util.ColorAnim(o.el, {
backgroundColor: {
to: '#ffffff'
}
});
animation.animate();
});
var callback = {
success: dataTable.onDataReturnReplaceRows,
failure: function() {
// error handling code
},
scope: dataTable
};
dataSource.setInterval(1000, null, callback);
dataTable.subscribe('cellFormatEvent',
function(o) {
YAHOO.util.Dom.setStyle(o.el, 'backgroundColor', '#ffff00');
var animation = new YAHOO.util.ColorAnim(o.el, {
backgroundColor: {
to: '#ffffff'
}
});
animation.animate();
});
var callback = {
success: dataTable.onDataReturnReplaceRows,
failure: function() {
// error handling code
},
scope: dataTable
};
dataSource.setInterval(1000, null, callback);
This example will not work beceause you added an interval and this is not the right solution. Because the function will be called each time.

Categories

Resources