Cannot pass data to getJSON even with closures - javascript

I went through many solutions to this problem on SO and none of them work for me, unfortunately.
I want to dynamically load and place some elements on my page. HTML is generated by one of the scripts and the resulting template (for injecting data into it) is passed to getJSON method. I wanted to fill that template inside my callback method and then add it to the body.
The problem is getJSON function. I tried to pass panel value with closure and bind and none of them work.
init: function(panel) {
// closure
$.getJSON("scripts/getZones.php",
(function () { // closure
var p = panel;
return function(zoneData) {
if (zoneData.status == true) {
$.each(zoneData.values, function(i, item) {
console.log("Module.Control_manual: Adding zone " + item.id);
// filling panel with data here - but panel is undefined
});
} else {
// error!
console.log("Module.Control_manual: Error: " + item['message']);
}
}
})
);
}
What is wrong with my code? Thanks!

For closure, You must execute your function, then the getJSON function could get the right callback function
init: function(panel) {
// closure
$.getJSON("scripts/getZones.php",
// closure, you must execute it first, then the `getJSON` function could get the right callback function
(function() {
var p = panel;
return function(zoneData) {
if (zoneData.status == true) {
$.each(zoneData.values, function(i, item) {
console.log("Module.Control_manual: Adding zone " + item.id);
});
} else {
// error!
console.log("Module.Control_manual: Error: " + item['message']);
}
}
}());
);
}
And further more, you should only do this closure way when you want to store the panel,otherwise you can just use panel instead the callback.

or yo can using bind method, to pass a parameter to callback function like this:
function doAfterJson(panel){
var p = panel;
return function(zoneData) {
if (zoneData.status == true) {
$.each(zoneData.values, function(i, item) {
console.log("Module.Control_manual: Adding zone " + item.id);
// filling panel with data here - but panel is undefined
});
} else {
// error!
console.log("Module.Control_manual: Error: " + item['message']);
}
}
}
}
//use 'bind(scope,parameter)' to pass scope and parameter to callback function
init: function(panel) {
// closure
$.getJSON("scripts/getZones.php",doAfterJson.bind(this,panel));
}

Related

How to only invoke a function that's called from within another function when a specific div is NOT clicked?

I am calling campusInfo(id) a few places in my code.
Inside campusInfo(id) function I am calling campusInfo_2(HouseID).
One of the instances where I am calling campusInfo(id) is on the onclick of a div.
In this case I don't want campusInfo_2(HouseID) invoked.
Here is the campusInfo(id) javascript function which calls campusInfo_2(HouseID)
//campus information
function campusInfo(id) {
fetch(`https://api/` + id)
.then(r => r.json())
.then(r => {
const mapClassToResponse = {
'.campus-name': 'name',
'.program-levels-values:eq(4)': 'annualTuition'
};
var HouseID = r['Houses'][0]['HouseID'];
Object.keys(mapClassToResponse).map(k => {
$(k).html(r[mapClassToResponse[k]]);
});
//formatting numbers to have commas
$(".program-levels-values:eq(4)").digits();
$(".campus-number:eq(0)").digits();
$(".campus-number:eq(1)").digits();
//program-levels
$(r.programLevels).each(function (index, item) {
$('.program-levels-values:eq(0)').append(item.programLevel + ' ');
});
//institution control
if (r.isInstitutionControlPublic == false) {
$('.program-levels-values:eq(2)').html('Private');
} else {
$('.program-levels-values:eq(2)').html('Public');
}
campusInfo_2(HouseID);
}).catch(console.log);
};
Here is the div where I don't want campusInfo_2(HouseID) being invoked
<div class="other-school" onclick="campusInfo(10)"><label
class="other-schools-list">Portland State University</label</div>
Here is what I tried thus far but it was saying in console that the element was undefined.
if(document.getElementsByClassName('other-school').clicked == false)
{
campusInfo_2(HouseID);
}
One suggested solution would be to provide an additional argument.
<div class="other-school" onclick="campusInfo(10, false)">
Then your method definition would change to:
function campusInfo(id, skipSecondCampusInfo) {
If you only pass that in on the inline binding, then it will be undefined on all the other calls. So then your call could be surrounded with:
if (skipSecondCampusInfo !== false) campusInfo_2(HouseID);
In which case if it is undefined, it will execute it. Otherwise, if it is false, it will not.

Return Function Arguments from another Function

I am trying to streamline some code but am hitting errors when I am converting something to a function. I am effectively trying to create one function that takes a single input, and then returns three things that end up as three arguments for another function.
Note: the final function that will take three arguments is a method titled md.use()
The original (working) code is as follows:
md.use(require('markdown-it-container'), 'warning', {
render: function (tokens, idx) {
var m = tokens[idx].info;
if (tokens[idx].nesting === 1) {
return '<aside class="warning">' + md.utils.escapeHtml(m[0]);
} else {
return '</aside>\n';
}
}
});
My attempt at streamlining it / making it reusable is:
function aside(name) {
return [require('markdown-it-container'), name, {
render: function (tokens, idx) {
var m = tokens[idx].info;
if (tokens[idx].nesting === 1) {
return '<aside class="' + name + '">' + md.utils.escapeHtml(m[0]);
} else {
return '</aside>\n';
}
}
}]
}
md.use.apply(null, aside('warning'));
This creates the following error when I try to build:
TypeError: Cannot read property 'block' of null
at Function.container_plugin (/Users/Paul/Development/shins/node_modules/markdown-it-container/index.js:138:6)
at MarkdownIt.use (/Users/Paul/Development/shins/node_modules/markdown-it/lib/index.js:496:10)
In your first attempt, the call to use() is direct through the md instance, hence invoked in the context of md (this === md).
In your second attempt, you're using md.use.apply(null) that's invoking use() without its original context (which is the md instance).
Try this instead:
md.use.apply(md, aside('warning'));
See MDN

Push() not working when called from inside getJSON()

I'm currently using the jQuery drag and drop formbuilder plugin. Now, I want to fetch drag and drop elements from my database and add those element to my drag and drop builder.
This is my request function:
function getElements(){
$.getJSON( getContextPath() + "/api/elements/get", function (data){
$.each(data, function(key, val) {
addElement();
});
});
}
Now, I want to add them to the formbuilder instance (I use this example and I use some pre-defined values):
function addElement(){
if (!window.fbControls) window.fbControls = [];
window.fbControls.push(function(controlClass) {
console.log("HERE");
class controlStarRating extends controlClass {
static get definition() {
return {
icon: '🌟',
i18n: {
default: 'Star Rating'
}
};
}
configure() {
this.js = '//cdnjs.cloudflare.com/ajax/libs/rateYo/2.2.0/jquery.rateyo.min.js';
this.css = '//cdnjs.cloudflare.com/ajax/libs/rateYo/2.2.0/jquery.rateyo.min.css';
}
build() {
return this.markup('span', null, {id: this.config.name});
}
onRender() {
let value = this.config.value || 3.6;
$('#'+this.config.name).rateYo({rating: value});
}
}
// register control
controlClass.register('starRating', controlStarRating);
return controlStarRating;
});
}
Unfortunately, my console.log("HERE") is not called, so it seems like it alle stops there. The weird thing is, if I use this as my request function the function is properly executed:
function getElements(){
var allElementsData = ["test"];
// $.getJSON( getContextPath() + "/api/template/get", function (data){
// });
$.each(allElementsData, function(key, val) {
addElement();
});
}
What is the problem?
In the function addElement you are pushing another function into the array window.fbControls. But that second function is only placed in the array not executed. You could for example execute it with this statement: window.fbControls[0](). Then you would see the console.log("HERE")

How to pass a function variable to a jQuery event on a Backbone model?

I have some problems with Backbone.Validation library, as you know model.bind("validated", function(...) ). I have that one in method of Reactjs Component. You can see the code as below, I would like to set the eMsg global var but when it is outside of user.bind or user.on, eMsg var is not assigned.
How can I get error from user.bind or user.on to outside of the statement?
handleSubmit : function() {
var self = this;
var user = new global.Models.RegisterModel(this.state.model, {validate:true});
var flag = false;
var eMsg = null;
********* (First Testing) ************
/*user.bind("validated", function(isValid, model, error) {
if(!isValid){
for (var k in error) {
eMsg += ", " + error[k];
}
}else{
flag = true;
}
});*/
********* (Sencod Testing) ************
if( user.isValid() ){
console.log("success")
this.setState({msg : "success"})
}else{
console.log("error")
user.on("invalid", function(model, error) {
console.log("invalid")
console.log(error);
})
this.setState({msg : eMsg})
}
}
You can't reference eMsg because the context of your on callback is no longer in the scope of your handleSubmit function. To bring the scope back you have two options.
Use jQuery.proxy()
You can bind the context of the object handleSubmit belongs to by using jQuery.proxy(), like this:
user.on("validated", $.proxy(function(isValid, model, error) {
if(!isValid){
for (var k in error) {
this.eMsg += ", " + error[k];
}
} else{
flag = true;
}
}, this));
Now, notice that I pre-pended this. to your eMsg variable, because there is no way to access local variables of a function outside the function. So here, you have to set up eMsg as a property of your object, i.e.
handleSubmit : function() {
this.eMsg = "";
...
}
All $.proxy() does is bind the $.on callback function to the context that we give it. Here, obviously it was this the context of the object that includes the 'handleSubmit` property.
Oldschool way (no jQuery)
We could also just pass in a reference to the object itself, which you already stored in the self variable. Our delegate would look like,
user.on("validated", function(isValid, model, error) {
if(!isValid){
for (var k in error) {
self.eMsg += ", " + error[k];
}
} else{
flag = true;
}
});
where we removed the $.proxy() function and used the object context stored in self, which is in scope in the closure where you defined your delegate. Again, note that eMsg has to be instantiated as a property (this.eMsg) in your object.
Using this.eMsg
Now all you have to do to retrieve the error message is refer to this.eMsg and your code would look like:
if( user.isValid() ){
console.log("success")
this.setState({msg : "success"})
} else {
console.log("error")
user.on("invalid", function(model, error) {
console.log("invalid")
console.log(error);
})
this.setState({msg : this.eMsg})
}

What's the difference between default event handler as an anonymous function vs function name

I'm modifying an a version of fancybox so that I do it bit of code before the run function.
I suppose I could put the code in the run function but I'm wondering why I can do the following
Original line that I'm concerned with:
D.undelegate(selector, 'touchstart.fb-start click.fb-start').delegate(selector, 'touchstart.fb-start click.fb-start', run);
I would like to do the following:
D.undelegate(selector, 'touchstart.fb-start click.fb-start').delegate(selector, 'touchstart.fb-start click.fb-start', function(){ /*do something */ run(); });
Unfortunately I get a cannot load error when I try to run the run function like this.
What's the difference between having a default handler vs a function call here? I understand that it will be used as a callback in former sense, but shouldn't it be the same here? Or is there some default parameters being passed into run behind the scenes. I've tried pass this, i.e., run(this) but fancybox still fails. Any ideas?
Larger piece of original source:
// jQuery plugin initialization
$.fn.fancybox = function (options) {
var opts = options || {},
selector = this.selector || '';
function run() {
var group = [], relType = false, relVal = $(this).data('fancybox-group');
// Check if element has 'data-fancybox-group' attribute, if not - use 'rel'
if (typeof relVal !== 'undefined') {
relType = relVal ? 'data-fancybox-group' : false;
} else if (this.rel && this.rel !== '' && this.rel !== 'nofollow') {
relVal = this.rel;
relType = 'rel';
}
if (relType) {
group = selector.length ? $(selector).filter('[' + relType + '="' + relVal + '"]') : $('[' + relType + '="' + relVal + '"]');
}
if (group.length) {
opts.index = group.index(this);
F.open(group.get(), opts);
} else {
F.open(this, opts);
}
return false;
}
if (selector) {
D.undelegate(selector, 'touchstart.fb-start click.fb-start').delegate(selector, 'touchstart.fb-start click.fb-start', run);
} else {
$(this).unbind('click.fb-start').bind('click.fb-start', run);
}
return this;
};
Try this:
D.undelegate(selector, 'touchstart.fb-start click.fb-start')
.delegate(selector, 'touchstart.fb-start click.fb-start',
function(){
/*do something */
run.call(this);
});
You have to make sure that the "run" function gets this set the way it was written to expect it. In other words, the library makes sure that the function that's invoked gets this set to something relevant (the fancybox object, or whatever). When you interpose an anonymous function like that, then your function is also invoked like that. You need to pass on that value of this to the function that was already there.

Categories

Resources