I'm running a bit of javascript code in a loop, creating a chart (using google visualization). After all this code is run, I need to access these objects somewhere else. My problem is that I can't just call "chart" again, since it now has been over-written.
I've managed to figure out a really hacky solution which involves using the MVC # thing to dynamically generate something at run-time, which I then force as a variable name. It works, but I don't think it's the right way to approach this problem. Is there a way for me to dynamically change the name of the variable each time it's run?
The following code is run multiple times.
#{
var myChart = "cData" + Model.ChartId.ToString();
}
...
function () {
#myChart = new google visualization.ChartWrapper({ ... });
dashboard.bind(slider, #myChart);
dashboard.draw(data);
}
myChart changes every single time the code is run, giving me a really hacky string. By putting it without the ' marks, it becomes a variable at runtime.
After all that is run, I have a resize function which run the following:
#myChart .setOption('width', width);
#myChart .setOption('height', height);
#myChart .draw();
This is also super hacky. I took advantage of how javascript ignores spaces, so that I can treat #myChart like an object.
The result is riddled with syntax errors, but it still runs, and it runs with intended results. So my question is if there is a "proper" solution for this, instead of making separate javascript functions for each individual chart I need to make.
Thanks.
There's nothing wrong with what you've done, but it could be better.
First, you don't have to rely on a space to mix JavaScript and Razor.
#(myChart).setOption('width', width);
But that's still ugly.
Approach 1
<script>
// global (usual caveats about globals apply)
var charts = {};
</script>
#for( int i = 0; i < 10; i++ )
{
string chartId = "chart-" + i;
<script>
charts['#chartId'] = new Chart();
charts['#chartId'].setOption('width', width);
charts['#chartId'].setOption('height', height);
charts['#chartId'].draw();
</script>
}
Approach 2
<script>
function doResize(chart) {
chart.setOption('width', width);
chart.setOption('height', height);
chart.draw();
}
</script>
#for( int i = 0; i < 10; i++ )
{
<script>
(function () {
// limit the scope of the "chart" variable
var chart = new Chart();
// perform other init
// listen for resize in some manner
$(window).on("resize", function() {
doResize(chart);
});
})();
</script>
}
Approach #2 is my preferred method as it manages scope and is clear to read.
Related
I'm hoping someone can help me out here. I'm writing some code that has several self-invoking functions. They all work a treat if I embed them on the html page (see code for one below) but if they (obviously minus the surrounding script tags) are included in my external javascript file instead....no joy, they don't invoke themselves. Any clue as to what might be going wrong if placed in the external file and why they don't invoke (and how to get them to do so!):
The self-invoking functions pulled off the html page:
<script>
(function () {
window.addEventListener("load", function() {
var d = new Date();
var n = d.toLocaleString();
var x = function dateDisplay(){
document.getElementById("dateDisplay").textContent = n;
}
x();
})
document.getElementById("dateDisplay").style.color = "red";
})();
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();
function closureCount() {
var hugeNum = Math.random();
var fixedNum = (hugeNum * 100) + 1;
var randomNum= Math.floor(fixedNum);
document.getElementById("p_r4").innerHTML = add() + " " + randomNum;
//code
}
</script>
Try a "less is more" approach:
Make sure <script> and </script> tags have been removed and are not present in the external js file. Script tags are valid in the HTML file only.
Do not attempt to access DOM elements before they are finalized, as in do it in the window load event listener. As per comment the second reference to the date display element needs moving. This could make the two self invoking functions look like:
(function ()
{ window.addEventListener("load", function()
{ var d = new Date();
var n = d.toLocaleString();
// access document elements after window load
document.getElementById("dateDisplay").textContent = n;
document.getElementById("dateDisplay").style.color = "red";
});
})();
var add = (function ()
{ var counter = 0;
return function ()
{ return counter += 1;
}
})();
(with '{' and '}' pairs under each other for debugging purposes, change to match your own style.)
I know this is an ancient message in the "Internet-timescale of things moving forward"... but since the question turned out when I was struggling with the same problem in the end of 2018, and there seems to be no accepted answer - or one that actually works, I decided to write a comment for the poor bloke who finds this in 2021 and might not be able to figure it out :)
In short; self executing functions from external files do work ... if the server provides correct MIME-type for the file; "text/javascript" (I'm assuming some of the older MIME-types that have been used for JavaScript-files should work equally well, but I never really tried them). One thing to note, though is that "text/plain" is one of the MIME-types that definitely does not work - and servers that determine the MIME-type based on output of "file"-command (or similar) will try to use that unless configured manually.
So - hope this helps :)
First, please excuse my bad English. I'm not use to write in English.
I'm using Node.js and i have variables that sometimes get their value from async functions and sometimes by direct assignment (ex:
async(function(data) {
var x= data.something
}; or x = 5;)
the problem is that later on the have shared code which forces me to duplicate the code.
in syncronius scripting i usually do an if.. else statement to seperate the cases and assign. ex:
if(boolivar){
var x = niceFunc();
}
else {
var x = 5;
}
coolFunc(x);
now days im forced to to this:
if(boolivar){
niceFUnc(function(X){
coolFunc(X);
}
}
else{
var x = 5;
coolFunc(X);
}
does someone has an idea how to solve my problem?
I thought about forcing the async function to be sync but:
a. i dont know how
b. it kind of ruins the whole point
I would do it essentially as you have, except that I would abstract the sync/async calls so that it doesn't make any difference to the code that's using it what's really happening behind the scenes (ignore the bad function names; I have no idea what your code does):
function doNiceFunc(boolivar, callback) {
if (boolivar) {
niceFUnc(function(x) {
callback(x);
});
} else {
callback(5);
}
}
Now doNiceFunc appears the same in both cases from the outside:
doNiceFunc(boolivar, function(x) {
coolFunc(x);
});
I've used this exact pattern in a library that retrieved data that was sometimes immediately available and other times had to be retrieved from the network. The users of this library didn't have to care which was the case at any given time, because the call looked the same in both situations.
I tend to use this lib node-sync
var sync = require('sync');
sync(function(){
var result = query.sync(query, params);
// result can be used immediately
})
I'm writing modular JavaScript and I have a certain function that does a whole lotta processing, viz. Draw 2 canvases, update a lot of variables and store object references. Now I want to execute another function which uses some of the variables updated above.
Something like this:
Paint canvases - Store image dimensions in variables (and a lot of other stuff)
Use those dimensions to do some math and geometry, update the canvases again! I can't do this math in the first function, as it is a common utility function I use to paint canvas, everywhere in my code.
If I inject a setTimeout in my code for 10 seconds, everything works fine, but without it, the second instruction above does not find the updated variables and hence fails.
Any way to work around this? Meaning, I want to execute the second instruction ONLY after some of the required variables are set. Synchronous execution, I say.
Note: I can't post any code here (or anywhere for that matter) as it is not allowed in my workplace!
For cases like this, I suggest to use jQuery and custom events. Simply post an event when the first function has finished updating the canvas. The second function (and anything else) can listen to these events and do whatever they want.
Pro:
No coupling
Individual parts are easy to test
Extensible
Con:
Needs jQuery or you'll need to extract the event handling code.
You could use getters and setters to watch for you for a given condition.
In the setter you can do some computations, check if some conditions are met
and update if required.
Just to give you an idea :
// updateFunc is the function called whenever a property changes
// and all conditions are met for an update.
// newProp1,2,3 are the new values for prop1,2,3
function MyStorageClass(updateFunc, newProp1, newProp2, newProp3 ) {
this.updateFunc = updateFunc;
this.prop1 = newProp1 ;
this.prop2 = newProp2 ;
this.prop3 = newProp3 ;
}
var MSCProto = MyStorageClass.prototype;
// update is needed if all properties are >0
MSCProto.checkUpdateRequired = function() {
return ( ( this.prop1 > 0 ) && (this.prop2 > 0) && (this.prop3 > 0) )
}
Object.defineProperty(MSCProto, 'prop1', {
get : function() { retrurn this._prop1},
set : function(x) { this._prop1 = x;
// and some other computations if need be
if (this.checkUpdateRequired()) this.updateFunc(); } };
Object.defineProperty(MSCProto, 'prop2', {
get : function() { retrurn this._prop2},
set : function(x) { this._prop2 = x;
// and some other computations if need be
if (this.checkUpdateRequired()) this.updateFunc(); } };
Object.defineProperty(MSCProto, 'prop3', {
get : function() { retrurn this._prop3},
set : function(x) { this._prop3 = x;
// and some other computations if need be
if (this.checkUpdateRequired()) this.updateFunc(); } };
I am not entirely sure how to phrase this question, but basically, I have a class, button that on its click should call the function passed to it.
button = function(...,callBack) {
//...
this._cb = callBack;
}
button.prototype.callBack = function(e) {
//...
this._cb();
}
and then somewhere else
//on canvas click
e.target.callBack(e);
(I hope this is about the right amount of background, I can give more if needed)
So the issue I am running into is when I dynamically instantiate the buttons such that their callbacks use data from an array. i.e.
for (var i = 0; i < levels.length; i++) {
buttons[buttons.length] = new button(..., function() {drawLevel(levels[i])});
}
Then when they are clicked, they run that callback code and try to find some random value for i (probably a for-loop that didn't use var) and runs that level.
My question is, how can I (without using eval) circumvent this problem.
Thanks!
I'm not 100% clear on what you're asking, but it looks like you're going to be getting the wrong value for i in the anonymous function you're creating in the loop (it will always be levels.length)
Way around this is to have a different scope for every function created, with the i in each scope being a copy of the i in the loop
buttons[buttons.length] = new button(..., (function(i){
return function() {drawLevel(levels[i])};
})(i));
I have various dom elements that have their contents updated at page load time. Typically they are updated by ajax with data from the server. They usually show things like charts and tabular information.
To keep things clean, I have reused the code that generates the final html for common tasks such as displaying a chart. I put options in the html attributes and have an attribute for the data url. e.g.
<div class="standard_chart"
chart_title="Trend of X vs. Y"
data_url="/data/x_vs_y"></div>
This organisation has significantly improved my js code.
The Problem
Sometimes I need to process the data first. For example, to filter it:
var filter_outliers_for_x_vs_y_data = function(data){
data = data.filter(function(d){
return -1000 < d.y && d.y < 1000;
});
return data;
};
I am loath to put an id attribute on the div just to re-write the data processing function for that particular dom id. Consider this:
$(document).ready(function(){
$('#my_x_vs_y_chart').each(function(){
// ... reworked code just for this element
});
});
I'd much rather, in that html document, put the pre-processing function right next to the html element (or in it). That seems like the best place for it.
What would be the best way to associate javascript with particular dom elements?
My ideas so far:
1.
<div class="standard_chart"
chart_title="Trend of X vs. Y"
data_url="/data/x_vs_y"
pre_process_fn="...some js code..."></div>
// then use `eval` to use the js code in the attribute
2.
<div class="standard_chart"
chart_title="Trend of X vs. Y"
data_url="/data/x_vs_y"
pre_process_fn="...some js code...">
<pre_process_fn>
<script>... function definitions ... </script>
</pre_process_fn>
</div>
// then somehow get and associate the js internal to the element
I'm not sure what the other options are, and what their relative benefits are.
You could use this idea, without rendering javascript into the attribute, but a fn name:
<div class="standard_chart"
chart_title="Trend of X vs. Y"
data_url="/data/x_vs_y"
preprocess_fn="preprocess_x_vs_y"></div>
And then define an object holding your preprocessor functions:
var preprocessors = {
"preprocess_x_vs_y": function () {
var dataUrl = $(this).attr("data_url");
var chartTitle = $(this).attr("chart_title");
// do the processing
}
};
When var preprocessors = { }; is predefined (in the head section), you can also later render the function into an html script element:
<script type="text/javascript">
preprocessors["preprocess_x_vs_y"] = function () {
var dataUrl = $(this).attr("data_url");
var chartTitle = $(this).attr("chart_title");
// do the processing
};
</script>
Now in the document ready event, do
$(document).ready(function () {
$('.standard_chart').each(function () {
var preproc_fn = $(this).attr("preprocess_fn");
// now call the preprocessor (with this === dom element)
if (preprocessors[preproc_fn] instanceof Function) {
preprocessors[preproc_fn].call(this);
}
});
});