javascript - sending a parameter to a function - javascript

I have this:
var MyObject = {};
MyObject.doStuff = function(someParam) {
var webdav = new Webdav("addr","port");
var handler = {
onSuccess: MyObject.Success,
onError: MyObject.Fail
}
webdav.PUT(handler, filename, options);
}
MyObject.Success = function(result) {
alert('status ' + result.status + result.statusstring);
}
I'm using exo platform javascript library for webdav access (if it matters)
The handler I'm creating will call MyObject.Success if webdav.PUT is done succesfully. How can i send the someParam to that function too?
Put in another way, after a successful or failed operation, I'm interested in doing something with the someParam, depending of the result.

This may be what you'r looking for:
javascript callback function and parameters
or maybe:
http://onemarco.com/2008/11/12/callbacks-and-binding-and-callback-arguments-and-references/
var someParam = 'foo';
var handler = {
onSuccess: function(result) {success(result, someParam);},
onError: function() { fail(); }
}

You should look into javascript objects, and try to contain the code within one scope. Something like this:
var MyObject = {
var doStuff = function(someParam) {
var webdav = new Webdav("addr","port");
var handler = {
onSuccess: function(result) {success(result, someParam);},
onError: function() { fail(); }
}
webdav.PUT(handler, filename, options);
}
var success = function(result, someParam) {
alert('status ' + result.status + result.statusstring);
}
var fail = function() {}
this.doStuff = doStuff;
}
var myObj = new MyObject();
myObj.doStuff(param);

One simple way to do it, taking advantage of JavaScript closures:
var handler = {
onSuccess: function(result) { MyObject.Success(result, someParam); },
onError: MyObject.Fail
}

Related

How to pass an ajax result to a Public veriable in the same class?

I have a jQuery file which also uses unserscore.js. It controls the selections of dates and different venues. For one of the pages it also controls which visuals are displayed depending on the type of venue. I can successfully, using ajax, get the type of page, but I have been unable to pass that value to a public variable in the script. It is based on which WiFi spot the data is coming from. If the data is from a local spot the page should display a d3 bubble chart. If it's from a remote spot it should display a map of the venue. Currently I have the functionality working with hard coding based on the id of the venue which is far from ideal.In order to make the decision based on which spot the venue is using I created an ajax call that gets the "spot". With console.log I can see that I am getting the correct result from the ajax call, but I'm missing something in terms of passing that information to a variable so I can use it.
This is the complete jQuery files:
define([
"ui/selects",
], function (SelectsUiClass) {
var global = this;
var MainControlsClass = function () {
// Private vars
var _this = this,
_xhr = null,
_selects = new SelectsUiClass(),
_dateRangeSelect,
_venueSelect,
_floorSelect,
_zoneSelect;
// Public vars
this.Selects = null;
this.spotName = null;
// Private Methods
var _construct = function () {
_dateRangeSelect = _selects.InitSelect('#mainControls-dateRange', _onSelectChange);
_venueSelect = _selects.InitSelect('#mainControls-venue', _onSelectChange);
_floorSelect = _selects.InitSelect('#mainControls-floor', _onSelectChange);
_zoneSelect = _selects.InitSelect('#mainControls-zone', _onSelectChange);
var value = _this.GetVenue();
_getChartDisplayDiv(value);
};
var _getChartDisplayDiv = function (venueId) {
var path = window.location.pathname,
pathArray = path.split("/"),
page = pathArray[pathArray.length - 1];
console.log('controlsjs 36, navigation page: ' , page);
console.log('controlsjs 37, venue value: ' , venueId);
_this.Load(venueId);
console.log('Controls 40, sPot Name = ', _this.spotName);
if (page === 'heatmap') {
if (venueId === 8 || venueId === 354) {
//make the bubble div visible
$("#heatmap-bubble").show();
//make the map div invisible
$("#heatmap-map").hide();
} else {
//make the map div visible
$("#heatmap-map").show();
//make the bubble div invisible
$("#heatmap-bubble").hide();
}
}
}
this.Load = function (venueId) {
console.log("Controls 66, Venue Id sent = ", venueId);
if (_xhr) {
_xhr.abort();
_xhr = null;
}
_this.SetLoading(true);
_xhr = $.ajax({
url: $("meta[name='root']").attr("content") + '/app/heatmap/spot',
type: 'POST',
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
data: {
venue_id: venueId
},
dataType: 'JSON',
async: true,
cache: false,
error: function (jqXHR, textStatus, errorThrown) {
_this.SetLoading(false);
},
success: function (response) {
_this.SetLoading(false);
console.log("Controls 90, Response of ajax call = ", response);
_this.Update(response);
}
});
};
// Public functions
this.SetLoading = function (option) {
if (_.isUndefined(option)) { option = false; }
if (this.spotName) { this.spotName.SetLoading(option); }
};
this.Update = function (data) {
if (_.isUndefined(data) || _.isNull(data)) {
console.log('Controls 106: Spot Name: ', data)
this.spotName = data;
}
};
var _getVenueData = function (venueId) {
for (var i = 0; i < venuesData.length; i++) {
if (venuesData[i].id === venueId) {
if (!_.isUndefined(venuesData[i].spot_data)) {
return venuesData[i].spot_data;
}
}
}
};
var _onVenueChange = function () {
var value = _this.GetVenue();
if (_.isNull(value)) {
return;
}
_getChartDisplayDiv(value);
//_setSelectValue(_venueSelect, value);
var venueData = _getVenueData(value);
console.log('Venue data received: ', venueData);
if (!_.isUndefined(venueData) && !_.isUndefined(venueData.floors)) {
_selects.UpdateSelect(_floorSelect, venueData.floors);
_onFloorChange();
}
};
var _onFloorChange = function () {
var value = _this.GetFloor(),
zones = [];
if (_.isNull(value)) {
return;
}
//_setSelectValue(_floorSelect, value);
if (_.isNumber(value)) {
var venueData = _getVenueData(_this.GetVenue()),
floors = venueData.floors;
for (var i = 0; i < floors.length; i++) {
if (floors[i].id === value) {
zones = floors[i].zones;
}
}
}
_selects.UpdateSelect(_zoneSelect, zones);
};
var _onZoneChange = function () {
var value = _this.GetZone();
if (_.isNull(value)) {
return;
}
//_setSelectValue(_zoneSelect, value);
};
var _onSelectChange = function (e) {
var t = $(e.target),
id = t.attr('id');
if (_venueSelect && _venueSelect.attr('id') === id) {
_onVenueChange();
} else if (_floorSelect && _floorSelect.attr('id') === id) {
_onFloorChange();
} else if (_zoneSelect && _zoneSelect.attr('id') === id) {
_onZoneChange();
}
EventDispatcher.Dispatch('Main.Controls.Change', _this, {
caller: id
});
};
// Public Methods
this.GetDateRange = function () {
return _selects.GetSelectValue(_dateRangeSelect);
};
this.GetDateRangeKey = function () {
if (_dateRangeSelect) {
var selected = _dateRangeSelect.find('option:selected');
if (selected.length) {
return selected.attr("data-key") || "";
}
}
return "";
};
this.GetVenue = function () {
return _selects.GetSelectValue(_venueSelect);
};
this.SetVenue = function (value) {
_selects.SetSelectValue(_venueSelect, value);
}
this.GetFloor = function () {
return _selects.GetSelectValue(_floorSelect);
};
this.SetFloor = function (value) {
_selects.SetSelectValue(_floorSelect, value);
}
this.GetZone = function () {
return _selects.GetSelectValue(_zoneSelect);
};
this.SetZone = function (value) {
_selects.SetSelectValue(_zoneSelect, value);
}
this.GetData = function () {
return {
dateRange: {
date: this.GetDateRange(),
key: this.GetDateRangeKey()
},
venue: this.GetVenue(),
floor: this.GetFloor(),
zone: this.GetZone()
};
};
// Init
_construct();
};
return MainControlsClass;
});
The function that determines which visual to display is close to the top: _getChartDisplayDiv:
var _getChartDisplayDiv = function (venueId) {
var path = window.location.pathname,
pathArray = path.split("/"),
page = pathArray[pathArray.length - 1];
_this.Load(venueId);
console.log('Controls 40, sPot Name = ', _this.spotName);
if (page === 'heatmap') {
if (venueId === 8 || venueId === 354) {
//make the bubble div visible
$("#heatmap-bubble").show();
//make the map div invisible
$("#heatmap-map").hide();
} else {
//make the map div visible
$("#heatmap-map").show();
//make the bubble div invisible
$("#heatmap-bubble").hide();
}
}
}
When I am able to pass the "spot" information to it or a variable that it uses, it should look like this:
var _getChartDisplayDiv = function (venueId) {
var path = window.location.pathname,
pathArray = path.split("/"),
page = pathArray[pathArray.length - 1];
_this.Load(venueId);
console.log('Controls 40, sPot Name = ', _this.spotName);
if (page === 'heatmap') {
if (_this.spotName === 'local' ) {
//make the bubble div visible
$("#heatmap-bubble").show();
//make the map div invisible
$("#heatmap-map").hide();
} else {
//make the map div visible
$("#heatmap-map").show();
//make the bubble div invisible
$("#heatmap-bubble").hide();
}
}
}
My ajax call is here:
this.Load = function (venueId) {
console.log("Controls 66, Venue Id sent = ", venueId);
if (_xhr) {
_xhr.abort();
_xhr = null;
}
_this.SetLoading(true);
_xhr = $.ajax({
url: $("meta[name='root']").attr("content") + '/app/heatmap/spot',
type: 'POST',
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
data: {
venue_id: venueId
},
dataType: 'JSON',
async: true,
cache: false,
error: function (jqXHR, textStatus, errorThrown) {
_this.SetLoading(false);
},
success: function (response) {
_this.SetLoading(false);
console.log("Controls 90, Response of ajax call = ", response);
_this.Update(response);
}
});
};
This successfully gets the right spot, but I have been unable to pass it to a variable I can use. I think I am getting mixed up between private and public variables. I tried to use the 'this.Update' function to pass the setting to the public 'this.spotName' variable, but that comes up null. I have also tried to simply return the result of the ajax call, but I get a "not a function" error. How can I make the result of the ajax call available to my '_getChartDisplayDiv' function?
Your problem is that you are trying to read the value of _this.spotName before it is assigned. Let us walk through the steps that happen.
When you call _getChartDisplayDiv(value), the _getChartDisplayDiv function first calls _this.Load(venueId). Load, in turn, submits an ajax request with a success callback, reproduced in abbreviated form below:
this.Load = function (venueId) {
// ...
_this.SetLoading(true);
_xhr = $.ajax({
...
success: function (response) {
_this.SetLoading(false);
console.log("Controls 90, Response of ajax call = ", response);
_this.Update(response);
}
});
};
When the response arrives, the success callback will be invoked, which in turn will call _this.Update, which will set the variable you are after. The syntax you used for this purpose is correct. However!
"When the response arrives" happens to be an unpredictable event in the future. It might be after 10 milliseconds, it might take 2 seconds, or the request might time out altogether. Even 10 milliseconds is already an eternity, compared to the time it takes your browser to execute all other code in your script. You can be quite sure that by the time $.ajax returns, the success callback has not run yet.
When you pass a callback (success) to a function ($.ajax) and the callback is not run before the function returns, this is called an asynchronous callback, "async" for short. When a callback might be invoked async, it is important for the function to guarantee that it always runs async, because this type of situation needs to be handled in an entirely different way from when the callback is invoked synchronously (i.e., before the function returns). You can read more about the technicalities in this blogpost. So this is exactly what $.ajax guarantees: it will never invoke the success (or error) callback before it returns, even in the hypothetical situation that the response would arrive fast enough.
Right after $.ajax returns, your Load function returns, at which point your _getChartDisplayDiv function continues to execute. Almost immediately after that, you intend to read _this.spotName. $.ajax has already returned, so you might hope that at this point, the success callback has already been invoked.
Unfortunately for you, async callbacks are more stubborn than that. Not only does an async callback not run until the function to which you pass it returns; it does not run until any currently executing function returns. Besides $.ajax, Load needs to return, _getChartDisplayDiv needs to return, any function that was calling _getChartDisplayDiv needs to return, and so forth. The entire call stack needs to unwind. Only then (and when the response actually arrives, which is likely to be many milliseconds later) will the success callback be invoked. This game rule is called the event loop in JavaScript.
The solution is simpler than you might expect: you just need to invert the order of control. Rather than trying to force the data out of a request when you want to update the chart, you can update the chart when the response arrives, and rather than trying to update the chart directly, you can just trigger the request. Specifically in your case, you just need to make three changes:
In the places where you currently call _getChartDisplayDiv, call _this.Load instead.
Remove the line that calls _this.Load inside the _getChartDisplayDiv function.
At the end of the success handler, add a line that calls _getChartDisplayDiv.
Incidentally, using a proper application framework will make it much easier to manage this kind of thing. In your case, I recommend trying Backbone; it builds on top of Underscore and jQuery and it is unopinionated, so you can gradually adopt it without having to radically change the way you work.
I am not familiar with underscore.js. For jQuery you have two options, which you can use as an inspiration for your case. Untested code:
1. Callback function
You provide a callback function:
$('.mydiv').myPlugin({ // Pass options Object to plugin
venuId: '123',
getType: function(type) {
console.log(type); // Example accessing internal data
}
});
Your plugin code:
(function( $ ) {
$.fn.myPlugin = function(opt) {
this.filter('div').each(function() {
const settings = $.extend({
namespace: 'myPlugin',
type: 'local'
getType: function() {},
// otherSettings: 'as needed',
}, opt);
// plugin code here...
if(typeof settings.getType === 'function') {
settings.getType(settings.type);
}
});
return this;
};
}( jQuery ));
2. Plugin method
You define plugin method(s) that can be called:
$('#mydiv').myPlugin({ // Pass options Object to plugin
venuId: '123'
});
console.log($('#mydiv').myPlaugin('getType'));
Your plugin code:
(function( $ ) {
$.fn.myPlugin = function(opt) {
this.filter('div').each(function() {
const settings = $.extend({
namespace: 'myPlugin',
type: 'local',
// otherSettings: 'as needed',
}, opt);
this.getType = function() {
return settings.type;
}
let firstArg = arguments[0];
if(typeof firstArg === 'string') {
let func = this[firstArg];
if(typeof func === 'function') {
var args = [];
for(var i = 1; i < arguments.length; i++) {
args.push(arguments[i]);
}
return func.apply(this, args);
}
} else {
// plugin init code here...
}
});
return this;
};
}( jQuery ));

Sending json via ajax in wordpress using Vanilla JS

I am sending a json in my server using vanilla JS and it returns a bad request, it seems the server only wants a key value pair like 'page=pageData&action=act', when i do this it works, but i would want to send data that way. Is there a way to make it possible?
When i try to make it in jquery it works fine.
$('.more-headlines').on('click', function() {
var pageData = $(this).data('page');
var pageURL = $(this).data('url');
var act = 'load_more';
var jsondata = {
page : pageData,
action : act
}
var xhr = new XMLHttpRequest();
xhr.open('POST', pageURL, true);
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr.onload = function() {
if (xhr.status >=200 && xhr.status < 400) {
var data = JSON.parse(xhr.responseText);
console.log(data);
} else {
console.log('sad');
}
};
xhr.send(JSON.stringify(jsondata));
});
This is my code in jquery
$('.more-headlines').on('click', function () {
var that = $(this);
pageData = $(this).data('page');
newPage = pageData+1;
pageURL = $(this).data('url');
act = 'load_more';
that.addClass('icon-spin');
that.find('span').html('loading headline');
jsondata = {
page : pageData,
action : act
}
$.ajax ({
type: 'POST',
url: pageURL,
data: jsondata,
success: function(response) {
setTimeout( function () {
that.data('page', newPage);
$('#featureOnDemand ul').append(response);
that.removeClass('icon-spin');
that.find('span').html('See more headlines');
}, 500);
}
});
});
I looked at the network tab in chrome and i saw that the send request becomes a key value pair like 'page=pageData&action=act'.
I am stuck in this part because i want to make a vanilla js ajax request in my project. Any idea would be much appreaciated. Many thanks!
You want to serialize your object data. Here's a helper function you can pass your object into:
var serializeObject = function (obj) {
var serialized = [];
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
serialized.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]));
}
}
return serialized.join('&');
};

Node.js - Can't send headers after they are sent

The closest issue I've found to mine is here. I believe I'm getting this error from how my .end() calls are set up. Here's the code we're working with:
app.get('/anihome',function(req,res){
var context = {};
function renderPage(context) {
res.render('anihome',context);
}
function addRequestToPage(text) {
context.data = text.toString('utf8');
context.info = JSON.parse(text);
return context;
}
function addAnimeToPage(text) {
context.anime = JSON.parse(text);
return context;
}
function addAnimeRequest(context) {
var options2 = {
host: 'anilist.co',
path: '/api/anime/20631?access_token=' + context.info.access_token,
method: 'GET'
};
https.request(options2, function(restRes) {
restRes.on('data',function(jsonResult) {
//context.anime = JSON.parse(jsonResult);
//console.log(JSON.parse(jsonResult));
console.log(context);
renderPage(context);
});
}).end();
}
function addHeaderRequest(context) {
var options = {
host: 'anilist.co',
path: '/api/auth/access_token?grant_type=client_credentials&client_id='
+ clientID + '&client_secret=' + secretKey,
method: 'POST'
};
https.request(options, function(restRes) {
restRes.on('data', function(jsonResult) {
context = addRequestToPage(jsonResult);
addAnimeRequest(context);
});
}).end();
}
addHeaderRequest(context);
});
I've tried setting up one of the .end()s with a callback, .end(addAnimeRequest(context));, which leaves me with a socket hang up error, so presumably something in my addAnimeRequest function is taking too long?
Is there a better way to make multiple requests to the same website with different options? I'm pretty new to Node.js.
The data event can be emitted more than once. You would need to add a listener for the end event and then pass in all of your data. Example:
https.request(options2, function(restRes) {
var buf = ''
restRes.on('data',function(jsonResult) {
//context.anime = JSON.parse(jsonResult);
//console.log(JSON.parse(jsonResult));
buf += jsonResult
});
restRes.on('end', function() {
// TODO JSON.parse can throw
var context = JSON.parse(buf)
renderPage(context)
})
}).end();

callback function does not appear to be executing

I have the following code grabbing a JSON object from github and I am tring to add certain parts to an array.
function getTree(hash) {
var pathToTree, returnedJSON;
pathToTree = 'https://api.github.com/repos/myaccount/myrepo/git/trees/' + hash;
$.ajax({
accepts: 'application/vnd.github-blob.raw',
dataType: 'jsonp',
url: pathToTree,
success: function (json) {
returnedJSON = json;
},
error: function (error) {
console.debug(error);
}
});
return returnedJSON;
}
function parseTree(hash) {
var objectedJSON, objectList = [], i, entry;
objectedJSON = getTree(hash, function () {
console.debug(objectedJSON); // this is not appearing in console
for (i = 0; i < objectedJSON.data.tree.length; i += 1) {
entry = objectedJSON.data.tree[i];
console.debug(entry);
if (entry.type === 'blob') {
if (entry.type.slice(-4) === '.svg') { // we only want the svg images not the ignore file and README etc
objectList.append(i.content);
}
} else if (entry.type === 'tree') {
objectList.append(parseTree(getTree(entry.sha)));
}
}
});
return objectList;
}
$(document).ready(function () {
var objects = parseTree('master', function () {
console.debug(objects);
});
});
I have the code retrieving the JSON object fine but I run into trouble when trying to get it parsed (aka pulling out the bits I want). The callbacks I am using do not seem to be going and was wondering if someone could look it over and help me out.
Specifically, can I add a callback to any function I choose? Do I have to do anything to that function?
I have fixed the code to illustrate how you would go about it.
function getTree(hash, cb) {
// notice that I copy the callback and hash references to have access to them in this
// function's closure and any subsequent closures, like the success and error
// callbacks.
var pathToTree, returnedJSON, cb = cb, hash = hash;
pathToTree = 'https://api.github.com/repos/myaccount/myrepo/git/trees/' + hash;
$.ajax({
accepts: 'application/vnd.github-blob.raw',
dataType: 'jsonp',
url: pathToTree,
success: function (json) {
returnedJSON = json;
// if anything was passed, call it.
if (cb) cb(json);
},
error: function (error) {
console.debug(error);
// an error happened, check it out.
throw error;
}
});
return returnedJSON;
}
function parseTree(hash) {
var objectedJSON, objectList = [], i, entry;
objectedJSON = getTree(hash, function (objectedJSON) {
console.debug(objectedJSON); // this is not appearing in console
for (i = 0; i < objectedJSON.data.tree.length; i += 1) {
entry = objectedJSON.data.tree[i];
console.debug(entry);
if (entry.type === 'blob') {
if (entry.type.slice(-4) === '.svg') { // we only want the svg images not the ignore file and README etc
objectList.append(i.content);
}
} else if (entry.type === 'tree') {
objectList.append(parseTree(getTree(entry.sha)));
}
}
});
return objectList;
}
$(document).ready(function () {
var objects = parseTree('master', function () {
console.debug(objects);
});
});
As far as I can see it, you are not passing callback to your functions:
function getTree(hash) {
And you are using like:
objectedJSON = getTree(hash, function () {
Similarly this function does not have callback param:
function parseTree(hash) {
And you are using like:
var objects = parseTree('master', function () {
Modify your functions like this:
function getTree(hash, fn) { ... }
function parseTree(hash, fn) { ... }
And then call fn using fn() when needed.
Add a second parameter o getTree function. Something like
function getTree(hash, callback)
And use a "jsopCallback" parameter in your Ajax options
$.ajax({
...
jsopCallback: callback,
...

Prototype and Ajax.Request scope

I'm having trouble getting the correct scope within prototype's Ajax.Request class. What I'm trying to do is write a simple API which wraps ajax requests:
API = Class.create({
initialize:function(api_token)
{
this.api_token = api_token;
this.request_uri = new Template('/api/#{api_token}/#{resource}.json');
this.status = 0;
this.last_result = null;
},
some_api_call:function()
{
var result = this._request('resource', {'id':1});
// and so on...
},
_request:function(resource, params)
{
var uri = this.request_uri.evaluate({"api_token":this.api_token,"resource":resource});
new Ajax.Request(uri,
{
contentType:'application/json',
method:'get',
parameters:params,
onSuccess:function(r)
{
alert(this);
this.last_result = r.responseJSON;
this.status = r.status;
}
});
return this.last_result;
}
});
When I'm in the onSuccess() method I expected +this+ to refer to the parent object, but it is giving me DOMWindow. I can't seem to get that response data into the API class at all. I figure it is something stupid (binding?), but I just can't seem to think this one out today.
Thanks
Okay. I missed the bigger problem. I was requesting asynchronously so it was setting the result, just not immediately. To be fair, it was also a binding issue. Here is the proper request:
_request:function(resource, params)
{
var uri = this.request_uri.evaluate({"api_token":this.api_token,"resource":resource});
new Ajax.Request(uri,
{
asynchronous: false,
contentType:'application/json',
method:'get',
parameters:params,
onSuccess:function(r)
{
this.last_result = r.responseJSON;
this.status = r.status;
}.bind(this)
});
alert(this.status);
return this.last_result;
}
Your solution should NOT work?
You need to use local variables above the nested function and then convert them into the 'this' scope:
_request:function(resource, params)
{
var uri = this.request_uri.evaluate({"api_token":this.api_token,"resource":resource});
var last_result = "";
var status = "";
new Ajax.Request(uri,
{
asynchronous: false,
contentType:'application/json',
method:'get',
parameters:params,
onSuccess:function(r)
{
last_result = r.responseJSON;
status = r.status;
}
});
this.last_result = last_result;
this.status = status;
alert(this.status);
return this.last_result;
}

Categories