I'm trying to use a variable inside my getJSON function. How can I pass it in? This is what I've tried.
function getComments(parent_id) {
if(typeof(parent_id)==='undefined') parent_id = -1;
console.log("parent_id type: " + typeof(parent_id)); // parent_id type: number
var message = { parentMessageID: parent_id };
$.getJSON(UPDATECOMMENTCALL, message, function(data, parent_id) {
console.log("JSON parent_id: " + typeof(parent_id)); // JSON parent_id: string
}
}
Instead of parent_id being -1 inside the function, it's a string with value "success". How can I pass it in properly?
You can just use it, don't put it as a parameter inside the callback function of the getJSON, just use it as a variable, because it's already global.
You callback function parameter has a higher priority than the scope variable with the same name (parent_id). So by accessing it inside the callback function you're actually refering to the function parameter rather than the variable you've defined outside of the function. Changing the name of the callback variable will fix it. For example:
var parent_id = -1;
console.log("parent_id type: " + typeof(parent_id)); // parent_id type: number
var message = { parentMessageID: parent_id };
$.getJSON(UPDATECOMMENTCALL, message, function(data, p_id) { // changed to p_id
console.log("JSON parent_id: " + typeof(parent_id)); // JSON parent_id: string
}
Generally, it's a good idea to avoid using the same names thus staying away from all the duplicate name mess.
Simple example to show you what's going on:
var a = 1;
function f(a) {
console.log(a);
}
Think what will f(2); output.
Related
I am trying to write a filter function that takes 2 parameters:
id type and the actual id value. Using these IDs, I want to filter an array of objects.For example, here I am trying to get a new array that only includes the values with the name of 'Mike'.
object:
var sample = [
{ name: 'Mike'},
{ name: 'John'}
];
filter function:
function filterById(obj, parameter, id) {
return obj.parameter == id;
}
this:
console.log(sample.filter(filterById(name, 'Mike')));
returns name is not defined.
Do I need to pass in the actual array as well? Is it possible to pass parameters into filter functions at all?
You would need to pass the "parameter" as a string too, and use the square bracket notation, and for this all to work your filterById function would itself have to return a function which matches the function used by Array.prototype.filter:
var sample = [
{ name: 'Mike'},
{ name: 'John'}
];
function filterById(parameter, id) {
return function(obj){
return obj[parameter] == id;
}
}
console.log(sample.filter(filterById('name', 'Mike')));
You don't have to invoke the function by yourself – it is a high-order function, so you have to provide only function. And here we come to the problem – you want to pass arguments there, but you can't!
So, there are few approaches. The first one is just to return another function, which will keep data in closure:
function filterById(parameter, id) {
return function(item) {
return item[parameter] == id;
}
}
The second option is to create another function via .bind, which is close to the idea of partial application. It will create new function with pre-defined parameters. They are always first, so you have to move actual item definition to the last position:
function filterById(parameter, id, item) {
return item[parameter] === id;
}
// we can create function for the future reference:
const filterByMike = filterById.bind(null, 'name', 'Mike');
sample.filter(filterByMike);
It's hard to say what is better, but I'd personally prefer the second approach.
I am currently writing a small Javascript Object which will add click listeners onto certain elements which then trigger an AJAX call to a PHP function. This is all working fine however, I want to call a function when the AJAX responds. I have made this happen by passing a function to the AJAX call which will be triggered when the response is given.
The problem I am having is that I am losing the scope of the object when passing through the protoytype as a call back (in order to stop the aynschronous problems that can occur with AJAX calls). The 'this' object (or self) is set to the window and not the instance of the object I have created. Here is my code:
//Rating Submit
var Rater = function(element, videoId, ratingStars, userId) {
this.userId = userId,
this.element = element;
this.videoId = videoId;
this.ratingStars = ratingStars;
var self = this;
jQuery(this.ratingStars).click(function(e) {
e.preventDefault();
self.ratingClick(this, self.changeStar);
});
}
Rater.prototype.ratingClick = function(item, changeStar) {
jQuery.ajax({
type : 'post',
dataType : 'json',
url : 'api/rate-video',
data : "userId=" + this.userId + "&videoId=" + this.videoId + "&rating=" + jQuery(item).attr("data-score"),
success : function(data) {
changeStar(data, item);
}
});
}
Rater.prototype.changeStar = function(response, item) {
var maxCount = jQuery(item).attr("data-score");
//console.log(self);
jQuery(self.ratingStars).each(function(key, value) {
alert(key);
});
}
As you can see, I am passing the 'self.changestar' prototype function to the AJAX call for this to be called when a response is given. When I try and access any of the variable I set in the constructor for that particular instance though, it says it is the Window object and not an instance of the class. Is it possible to pass through a prototype function as a call back from within the instance? I hope I have explained myself ok....
Thanks
The problem is that when you do this:
self.ratingClick(this, self.changeStar);
you have exactly the same problem you had in Rating with the jQuery click callback, which you solved with your self variable: Only the function reference, changeStar, gets passed, nothing about what value to use as this when calling it.
One solution is to use Function#bind, which you call on a function to get back another function that, when called, will call the original with a specific this value (and optional arguments):
self.ratingClick(this, self.changeStar.bind(self));
Alternately, you could pass the value to use as this separately:
self.ratingClick(this, self.changeStar, self);
...and then use Function#call in the success handler:
Rater.prototype.ratingClick = function(item, changeStar, thisArg) { // <== Change here
jQuery.ajax({
type : 'post',
dataType : 'json',
url : 'api/rate-video',
data : "userId=" + this.userId + "&videoId=" + this.videoId + "&rating=" + jQuery(item).attr("data-score"),
success : function(data) {
changeStar.call(thisArg, data, item); // <=== And here
}
});
}
Is there a way to provide a function to Handlebars that calculates the value when needed, instead of supplying all values up-front in the context?
For example, say I wanted to fill out any template with example data:
var exampleFunction = function (varName) {
return "Example " + varName;
};
If I know ahead of time what variables a Handlebars template needs, I could use this function to assemble a context. However, what I really want to do is:
var template = Handlebars.compile(templateString);
var html = template.fillFromFunction(exampleFunction);
Is this possible? If not, then are there any other template engines that support it?
Bonus question: can this be made asynchronous, e.g.:
var template = Handlebars.compile('{{foo.bar}}');
var dataFunction = function (path, callback) {
setTimeout(function () {
callback("Async " + path);
}, 100);
};
Short version:
It's a hack, but I've got a workaround.
var oldNameLookup = handlebars.JavaScriptCompiler.prototype.nameLookup;
handlebars.JavaScriptCompiler.prototype.nameLookup = function (parent, name) {
return '(typeof ' + parent + ' === "function" ? '
+ parent + '(' + JSON.stringify(name) + ') : '
+ oldNameLookup(parent, name) + ')';
}
Usage:
var template = handlebars.compile('{{foo}} {{bar}}');
var dataFunction = function (key) {
return 'data:' + key;
};
console.log(template(dataFunction));
// Outputs: "data:foo data:bar"
Sub-properties:
To enable sub-properties (e.g. "{{foo.bar}}"), we need a wrapper method:
function transformFunc(dataFunction) {
return function (key) {
if (typeof key !== 'string') {
return dataFunction('');
}
var pointerKey = '/' + key.replace(/~/g, '~0').replace(/\//g, '~1');
return transformFunc(function (path) {
return dataFunction(pointerKey + path);
});
};
}
Usage:
var template = handlebars.compile('{{foo}} {{foo.bar}}');
var dataFunction = transformFunc(function (path) {
// path is a JSON Pointer path
return "data:" + path;
});
console.log(template(dataFunction));
// Outputs: "data:/foo data:/foo/bar"
If one wanted, I suppose .compile() and .precompile() could be modified so that transformFunc() was applied to any incoming function when templating.
Explanation:
The hack includes altering the code-generation of Handlebars, but this code comment implies this is sort of OK. I tried finding a way to subclass, but couldn't see how to get this or this to use it.
Short version: override the nameLookup() method.
This method usually generates JavaScript code like depth0.foo or (depth1 && depth1.bar). We're extending it so the generated code first checks the parent to see if it's a function.
If it's a function, then it calls the function with the property name as the argument. Otherwise, it returns the same value as previously. For example, it will generate something like:
(typeof depth0 === "function" ? depth0("foo") : (depth0 && depth0.foo))
For simple variables (e.g. just "{{foo}}") you can now just supply a function and it will be called with the variable names.
Enabling sub-properties
However, for nested properties (i.e. "{{foo.bar.baz}}") we actually need our function to return another function, which can either return appropriate data or be called with another property name, depending which is needed.
So: if our transformed function is not given a string, then it is assumed we are at the end-point (we want the actual data, not a sub-property), so we just call through.
If our transformed function is given a string, then it's assumed to be a key, so another (transformed) function is return that calls back to the data function, prefixing the argument appropriately.
var currentbutton = {};
function setPreset(obj) {
try{
if(obj.name===name && obj.value===value){
//log.error("preset array's OID at position ["+index+"] is"+presets[index].name +" and the value stored is "+presets[index].value);
currentbutton.name=obj.name;
currentbutton.value=obj.value;
log.error("currentbutton name= "+currentbutton.name+ "currentbutton value= " + currentbutton.value );
}
else
log.error("adklafjklajkl");
}
catch(ie){
log.error("couldn't set preset");
}
presets.forEach(function(obj));
I know there must be mistakes in this code that I wrote, first of all, I was told that the function need to receive an object as an argument, which I have no idea how to pass it to the function. I tried google, but I did not find any relevant information on whether a function can receive an object as an argument.
presets is an array which contains objects which has two properties (called "name" and "value")
basically, the array Presets goes through its enumerated list of variables with forEach, and compare if the argument obj's name and value are identical or not to any of the objects stored inside the array, if they are identical, set the currentbutton's name and value to the one inside the argument obj. Then we will have other functions which will operate on currentbutton that i don't have to worry about.
I know it's not really clear because I am not even sure if that's what is wanted of me.
You don't quite understand how forEach works. The forEach method takes a function as its argument:
[1,2,3].forEach(function(item) {
alert(item);
});
That function passed into forEach is given an argument itself. Here, I've named it item. The forEach method repeatedly invokes the function and supplies a successive member of the array as the first argument each time it is invoked.
Now, instead of passing in a literal function, I can use a variable to hold my function:
var alertStuff = function(item) {
alert(item);
}
Then, I use that function (referring to it by variable name) in forEach:
[1,2,3].forEach(alertStuff);
// is the same as...
[1,2,3].forEach(function(item) {
alert(item);
});
Thus, you want to use presets.forEach(setPreset);.
Define a function which accepts a paramter
function myNewFunc(obj){
alert(obj.myFirstProp);
}
Define an object which we are going to pass as an argument to the above function
var myObject = {
myFirstProp: "testing"
};
Call the function and pass the object as an argument
myNewFunc(myObject);
Your brackets were screwed up and you invoked forEach wrong.
var presets = [
{name:'a', value:1},
{name:'b', value:2},
{name:'c', value:3},
];
var currentbutton = {};
function setPreset(obj) {
try{
if(obj.name===name && obj.value===value){
//log.error("preset array's OID at position ["+index+"] is"+presets[index].name +" and the value stored is "+presets[index].value);
currentbutton.name=obj.name;
currentbutton.value=obj.value;
log.error("currentbutton name= "+currentbutton.name+ "currentbutton value= " + currentbutton.value );
} else { // syntax error, opening { of else block was missing
log.error("adklafjklajkl");
}
} // syntax error, closing } of try block was missing
catch(ie){
log.error("couldn't set preset");
}
} // syntax error, closing } of function was missiong
presets.forEach(setPreset);
I'm receiving some 'body' content from a jquery's json call, where I can get the unique javascript element returned by doing:
script_element = $(data.body)[1]
This equals to:
<script type="text/javascript">
updater('foo', 'bar', {}, '0', constant='');
</script>
So, typeof script_element returns "object"
And, if I run script_element.innerText, I can get:
updater('foo', 'bar', {}, '0', constant='');
After receiving this script, what I'm doing right now is just run an eval on it, but searching around I couldn't get a way to run eval changing function call params.
What I'm trying to do is change the third param of the call, in this case the {}, that can change depending on the return of the json call, so I can't just search for {}.
I could also do script_element.text.split(',')[2] for example, and change this text on the fly, but I was thinking there should be a better way to do this.
I don't know if javascript can recognize and treat a "future method call", but still think there should be a better way.
Any idea?
What you could do is shadowing the function so as to be able to alter the third argument. You ought to define that shadowing function before fetching the JSON.
var originalUpdater = updater; // keep old function to call
// overwrite (shadowing)
updater = function(a, b, c, d, e) {
// change c appropriately here
originalUpdater(a, b, c, d, e);
}
Then you can still just eval it (which is not very safe, but that's not your point if I'm not mistaking), and it will call the shadow function.
A more generic shadowing method would be along the lines of:
var originalUpdater = updater; // keep old function to call
// overwrite (shadowing)
updater = function() {
// change arguments[2] appropriately here
originalUpdater.apply(this, arguments);
}
Fiddle: http://jsfiddle.net/n7dLX/
Change the server. Rather than returning
<script type="text/javascript">
updater('foo', 'bar', {}, '0', constant='');
</script>
Return
{
"method": "updater",
"params": [
"foo", "bar", {}, "0", ''
]
}
Assuming that you cannot change what is being sent over from the server, you can simply run through the innerText with a regular expression and pass update the HTML before you insert it.
var replacer = /\w+\(([^()]+)\)/gi;
script_element.innerText.replace(replacer, function(matched_text, func_params){
var orig_func_params = func_params;
// Make changes to func_params here.
return matched_text.replace(orig_func_params, func_params);
});
This can be functionized by doing the following:
var replacer = /\w+\(([^()]+)\)/gi;
function replace_arg(script_element, arg_index, replacement_value) {
script_element.innerHTML = script_element.innerHTML.replace(replacer,
function(matched_text, func_params){
var orig_func_params = func_params;
func_params = func_params.split(",");
if (arg_index >= func_params.length) {
throw new RangeError(arg_index + " is out of range. Total args in function:" + func_params.length);
}
func_params[arg_index] = JSON.stringify(replacement_value);
return matched_text.replace(orig_func_params, func_params.join(","));
});
return script_element;
}
This can be called in this way:
script_element = replace_arg(script_element, 3, {"new":"arg"});
I don't understand what you are doing, but in general if you don't want to rely on the order of parameters make the function take one parameter that is an object whose properties are the parameters:
function add(params) {
var a = params.hasOwnProperty("paramA") ? params.paramA : 0;
var b = params.hasOwnProperty("paramB") ? params.paramB : 0;
return a + b;
}
add({paramA: 1, paramB: 2});
In this case you should use hasOwnProperty to check if the function was passed the parameter you are looking for before trying to access it.