Extend a function stored in a variable - javascript

I have a large JavaScript project that makes several Ajax web service calls. The code to handle the web service calls comes from a shared external file.
To separate the web service from calling code, there is a global object to reference to the calling function like so
var doRemote ={};
$(document).ready(function(){
doRemote =getRemoteEndpoint('https://someplace.org/MyWebService.aspx');
}
A simplified version of the getRemoteEndpoint, which is in a file shared by several other pages in addition to the one I'm working on is as follows:
function getRemoteEndpoint(url) {
return function(methodName, options) {
var extension = {
url: url + '/' + methodName,
data: {},
async: true
};
var combined = $.extend({}, extension, options);
combined.data = JSON.stringify(combined.data);
return $.ajax( combined );
};
}
I invoke the web service calls by the following code
doRemote('WebServiceMethodName',
{
success: function(data) {
alert('Web Service Returned' + data);
},
error: function(req, stat, err) {
alert('Error');
}
});
I have the need to execute a function before executing the getRemoteEndpoint call in only the page I'm working on. Instead of calling the function before each of the 30 web service calls, I'd like to add a line of code to the function. I've tried to replace the doRemote assignment with the following.
doRemote =function() {
DoTask();
return getRemoteEndpoint('https://someplace.org/MyWebService.aspx');
};
DoTask is a named function in the program I'm working on. While it throws no errors, none of the Ajax calls work.
I tried to use the JQuery.extend function, but it didn't work either.
What am I doing wrong?

You have to actually call it to assign the result of getRemoteEndpoint to doRemote:
doRemote = (function() {
DoTask();
return getRemoteEndpoint('https://someplace.org/MyWebService.aspx');
})();
Update:
doRemote = (function() {
var oldDoRemote = getRemoteEndpoint('https://someplace.org/MyWebService.aspx');
return function(a1, a2) {
DoTask();
oldDoRemote(a1, a2);
}
})();

Related

AJAX Call to MVC5 ActionResult

I am trying to figure out what is wrong with the following code:
$(".ReportScore").click(function () {
$.ajax({
type: "GET",
url: "/events/Tournaments/ReportScore",
success: function() {
location.reload();
},
error: function() {
alert("The scores were not recorded");
}
});
});
When I type the url in the bar, it works without problems, however when I try to do an ajax call I get 404 Page not found error.
To clarify, when I click on the button I Get a popup saying "The scores were not recorded" and on developer tools I get a script error saying Page not found.
I also have a breakpoint in visusal studio on the method itself, but the point is never hit as the method is never called.
Server Side Code:
public async Task<ActionResult> ReportScore()
{
var a = "abc"
}
var a line is never hit.
EDIT:
I have another ajax call from the same script that works without problems:
$("#InvitedMember").autocomplete({
source: function (request, response) {
$.ajax({
type: "POST",
url: "/events/Teams/Members",
data: { id: $("#InvitedMember").val() },
success: function (data) {
response($.map(data, function (item) {
return {
label: item.CustomUrl, value: item.CustomUrl
};
}));
}
});
},
create: function () {
$(this).data('ui-autocomplete')._renderItem = function (ul, item) {
return $('<li>')
.append("<a><div>" + item.label + "</div></a>")
.appendTo(ul);
};
},
select: function (event, ui) {
//you can access ui.item to get the selected item object.
$("#InvitedMember").val(ui.item.value);
return false;
}
});
It is not a good idea to hardcode your url's like that. You should always use the Url.Action or Url.RouteUrl html helper methods to build the relative url to the action methods/endpoints you are accessing. These helper methods will take care of correctly building the url regardless of your current page/path.
Also, from your comment,it seems like events is the name of your virtual directory/application name in your IIS. You should not use those in your code to build the urls as it might change based on your deployment. What if you want a a copy of your code deployed to "http://staging.yourSite.com" ?
As long as you use the Url.Action helper method,it will build the correct relative url to your app and you do not need to worry about your IIS virutal directory/application name.
var url = "#Url.Action("ReportScore","Tournaments")";
$.ajax({
type: "GET",
url:url,
success: function (res) {
alert('success happened');
//location.reload();
},
error: function () {
alert("The scores were not recorded");
}
});
The above code will work if you have it in a razor view. But if your ajax call code is in external js file, You may build the relative url to the app root and pass that to your js file and use that to build the url. You can use the #Url.Content("~") to build the url to app root. If you want, you can build the url to specific action method itself.
<script>
var myApp = myApp || {};
myApp.Urls = myApp.Urls || {};
myApp.Urls.baseUrl = '#Url.Content("~")';
myApp.Urls.reportScoreUrl= '#Url.Action("ReportScore","Tournaments")';
</script>
<script src="~/Scripts/PageSpecificExternalJsFile.js"></script>
And in your PageSpecificExternalJsFile.js file, you can read it like
var myUrlToUser = myApp.Urls.reportScoreUrl;
alert(myUrlToUser);
or build using the base url.
var myUrlToUser= myApp.Urls.baseUrl+"Tournaments/ReportScore";
alert(myUrlToUser);
The problem seems not in your javascript code but your controller action method (if it is indeed how it is written)
public async Task<ActionResult> ReportScore()
{
var a = "abc"
}
Your code block doesn't show any return statement, make sure your return value is awaited.
i.e.
return await "abc"

AJAX call using IIFE? Can't seem to get .done to work the same as a regular function

I currently have an AJAX call out to a PHP file that works, that is the following:
//Load map from database
function getMap(){
return $.ajax({
url: "getMap.php",
type: "POST",
dataType: 'JSON',
});
};
getMap().done(function(r) {
if (r) {
loadedMap(JSON.parse(r.mapArray), JSON.parse(r.mapProperties)); //call loadedMap(r) if loading a map from DB
} else {
console.log("No data");
}
}).fail(function(x) {
console.log("error");
});
That works within a single javascript file that successfully passes r.mapArray and r.mapProperties to my main loadedMap function.
I'm trying to learn about the IIFE Javascript Module model, and split my work up into separate files.
So, I currently have main.js:
(function() {
// let's get our map
var gameMap = mapGen.getMap().done();
console.log(gameMap);
})();
and mapGen.js:
var mapGen = function() {
return {
getMap: function() {
return $.ajax({
url: "getMap.php",
type: "POST",
dataType: 'JSON',
});
}
};
}()
If I console.log(gameMap), I see the responseText JSON object with my data. I just can't seem to access it. console.log(gameMap.responseText) is undefined. as is gameMap.responseJSON (though I see both of them in the console).
Looking the code over it looks as the the separation of the files was not the issue and that looks to be implemented correctly. The issue is with how you are handling the AJAX request. The function mapGen.getMap() actually returns a jqXHR Object as opposed to the response that you are trying to access.
Just as you had in your previous file, you will need handle the response of your request.
(function() {
// let's get our map request
var gameMap = mapGen.getMap();
gameMap.done(function(r){ ... }).
fail(function(x){ ... });
})();
You will be able to access the response data you are looking for within the done() function.

Functions within a loop using requirejs

I'm having an issue with calling functions within a loop across different modules using requirejs. The function call within the loop resides in module A and executes a function in module B that fires off an Ajax request using jQuery. Each iteration of the loop fires off a different request with different arguments being passed to module B's function that fires off the Ajax request. When the success function of the Ajax request executes, I find that all my argument values are always the values of the last Ajax call made, for all 4 separate Ajax calls.
I've done some googling and it sounds like this is a pretty common problem when executing a function within a loop. The fix tends to be to break out the function call into a different function, creating a different scope. Since my loop and Ajax calls are in 2 different modules I had assumed this would solve that issue, however it still persists.
I've tried some solutions in other stack overflow posts like:
JSlint error 'Don't make functions within a loop.' leads to question about Javascript itself and How to pass parameter to an anonymous function defined in the setTimeout call? without success. Anyone have any idea?
Sample code for loop module A:
define(["mpos"],
function(mpos){
var monitor = {
startMonitoring : function(poolObj){
// Start Monitoring
$.each(mpos.msgs, function(action,callback){
poolObj.action = action;
mpos.sendApiRequest(poolObj,action,callback);
});
}
};
return monitor;
}
);
Sample code for Ajax module B - this module is referenced as mpos in module A
define(["mule","constants"],
function(mule,constants){
var mpos = {
sendMessage : function(postData,callback,$poolOut){
return $.ajax({
'type':'post',
'url':constants.URLS.proxy,
'data':{'url':postData},
success : function(data){
// if we have $poolOut we know this is a mpos call
if($poolOut != undefined){
var keys = Object.keys(data);
// add poolOut to data
data.poolOut = $poolOut;
var poolObj = $poolOut.data('poolObj');
if(poolObj){
var action = poolObj.action;
console.log(poolObj,action);
if(action){
if(action == "getuserstatus"){
mule.registerPool(poolObj);
}
} else {
log.error("No action on poolObj while attempting to calculate the need for a registerPool call");
}
}
}
// parse data
callback.apply(this, data);
},
error : function(x,h,r){ ... },
dataType : 'json'
});
},
sendApiRequest : function(poolObj,action,callback){
var url = poolObj.url + '&page=api&action=' + action;
var $poolOut = constants.cache.monitorOutput.find('.pool-out.' + poolObj.id);
var dfd = mpos.sendMessage(url,callback,$poolOut);
$.when(dfd).always(function(){
var refreshTimer = setTimeout(function(){
if(constants.state.monitorEnabled){
mpos.sendApiRequest(poolObj, action, callback);
}
}, poolObj.refreshRate);
});
},
msgs : {
"getuserstatus" : function(data){ ... },
"getpoolstatus" : function(data){ ... },
"getuserworkers" : function(data){ ... },
"getuserbalance" : function(data){ ... }
}
};
return mpos;
}
);
Thanks!
NOTE: I am assuming that $poolOut.data('poolObj') is being used to find the poolObj instance passed in the call to startMonitoring, and will return the same instance each time.
You state, "Each iteration of the loop fires off a different request with different arguments being passed to module B's function that fires off the Ajax request."
This statement is not correct. Each iteration fires off a different request with the first argument poolObj being the same in each iteration.
In your .each iteration, you are overwriting the value of poolObj.action before each call to sendApiRequest.
In the AJAX success handler, which is likely invoked after all iterations have completed, the value of poolObj.action will have the value you set it to in the last iteration.
To solve this, I think you need to pass action as a parameter to sendMessage, too, so that a separate value is being stored in the closure for each function call.
var mpos = {
sendMessage : function(postData,action,callback,$poolOut){
return $.ajax({
'type':'post',
'url':constants.URLS.proxy,
'data':{'url':postData},
success : function(data){
// if we have $poolOut we know this is a mpos call
if($poolOut != undefined){
var keys = Object.keys(data);
// add poolOut to data
data.poolOut = $poolOut;
var poolObj = $poolOut.data('poolObj');
if(poolObj){
// action is not guaranteed to be the same as poolObj.action here,
// since poolObj.action may have changed since this function was first called
console.log(poolObj,action);
if(action){
if(action == "getuserstatus"){
mule.registerPool(poolObj);
}
} else {
log.error("No action on poolObj while attempting to calculate the need for a registerPool call");
}
}
}
// parse data
callback.apply(this, data);
},
error : function(x,h,r){ ... },
dataType : 'json'
});
},
sendApiRequest : function(poolObj,action,callback){
var url = poolObj.url + '&page=api&action=' + action;
var $poolOut = constants.cache.monitorOutput.find('.pool-out.' + poolObj.id);
var dfd = mpos.sendMessage(url,action,callback,$poolOut);
$.when(dfd).always(function(){
var refreshTimer = setTimeout(function(){
if(constants.state.monitorEnabled){
mpos.sendApiRequest(poolObj, action, callback);
}
}, poolObj.refreshRate);
});
},
msgs : {
"getuserstatus" : function(data){ ... },
"getpoolstatus" : function(data){ ... },
"getuserworkers" : function(data){ ... },
"getuserbalance" : function(data){ ... }
}
};

Accessing outer scope

I'm working on creating a Users collection with the ability to then grab single users inside. This will be used to match from another system, so my desire is to load the users once, and then be able to fine/match later. However, I'm having a problem accessing the outer users collection from an inner method.
function Users(){
var allUsers;
this.getUsers = function () {
// ajax to that Jasmine behaves
$.ajax({
url: '../app/data/jira_users.json',
async: false,
dataType: 'json',
success: function(data) {
allUsers = data;
}
});
return allUsers;
};
this.SingleUser = function (name) {
var rate = 0.0;
var position;
this.getRate = function () {
if(position === undefined){
console.log('>>info: getting user position to then find rate');
this.getPosition();
}
$.ajax({
url: '../app/data/rates.json',
async: false,
dataType: 'json',
success: function(data) {
rate = data[position];
}
});
return rate;
};
this.getPosition = function () {
console.log(allUsers);
//position = allUsers[name];
return position;
};
//set name prop for use later I guess.
this.name = name;
};
}
and the test that's starting all of this:
it("get single user's position", function(){
var users = new Users();
var someone = new users.SingleUser('bgrimes');
var position = someone.getPosition();
expect(position).not.toBeUndefined();
expect(position).toEqual('mgr');
});
The getPosition method is the issue (which might be obvious) as allUsers is always undefined. What I have here is yet another attempt, I've tried a few ways. I think the problem is how the Users.getUsers is being called to start with, but I'm also unsure if I'm using the outer and inner vars is correct.
Though the others are correct in that this won't work as you have it typed out, I see the use case is a jasmine test case. So, there is a way to make your test succeed. And by doing something like the following you remove the need to actually be running any kind of server to do your test.
var dataThatYouWouldExpectFromServer = {
bgrimes: {
username: 'bgrimes',
show: 'chuck',
position: 'mgr'
}
};
it("get single user's position", function(){
var users = new Users();
spyOn($, 'ajax').andCallFake(function (ajaxOptions) {
ajaxOptions.success(dataThatYouWouldExpectFromServer);
});
users.getUsers();
var someone = new users.SingleUser('bgrimes');
var position = someone.getPosition();
expect(position).not.toBeUndefined();
expect(position).toEqual('mgr');
});
This will make the ajax call return whatever it is that you want it to return, which also allows you to mock out tests for failures, unexpected data, etc. You can set 'dataThatYouWouldExpectFromServer' to anything you want at any time.. which can help with cases where you want to test out a few different results but don't want a JSON file for each result.
Sorta-edit - this would fix the test case, but probably not the code. My recommendation is that any time you rely on an ajax call return, make sure the method you are calling has a 'callback' argument. For example:
var users = new Users();
users.getUsers(function () {
//continue doing stuff
});
You can nest them, or you can (preferably) create the callbacks and then use them as arguments for eachother.
var users = new Users(), currentUser;
var showUserRate = function () {
//show his rate
//this won't require a callback because we know it's loaded.
var rate = currentUser.getRate();
}
var usersLoaded = function () {
//going to load up the user 'bgrimes'
currentUser = new users.SingleUser('bgrimes');
currentUser.getRate(showUserRate);
}
users.getUsers(usersLoaded);
your approach to fill the data in allUsers is flawed
the ajax call in jquery is async so every call to users.getAllUsers would be returned with nothing and when later the success function of the jquery ajax is called then allUsers would get filled
this.getUsers() won't work. Its returning of allUsers is independent from the ajax request that fetches the data, because, well, the ajax is asynchronous. Same with getRate().
You'll have to use a callback approach, where you call getUsers() with a callback reference, and when the ajax request completes, it passes the data to the callback function.
Something like:
this.getUsers = function (callback) {
// ajax to that Jasmine behaves
$.ajax({
url: '../app/data/jira_users.json',
async: false,
dataType: 'json',
success: function(data) {
callback(data);
}
});
};
And the call would be along the lines of:
var user_data = null;
Users.getUsers(function(data) {
user_data = data;
});

Problem with jQuery Calls in a Javascript Class with Callbacks

I'm using JavaScript, jQuery, and JSONP to make asynchronous, cross-domain WCF service calls. I've gotten this functionality successfully working, so I know that the problem I'm experiencing is not on the service side.
I had my test client site set up to make inline jQuery calls using the getJSON method, and it was working fine. However, I then tried to take the jQuery calls and put them into a JavaScript class. Now I'm having trouble getting callbacks to fire.
This works fine (the functions in the working example are added in script tags on the page itself):
function handleResponse(result) {
if (result.Success) {
// do something
}
else {
// do something else
}
}
function validate(serviceURL, data) {
$.getJSON(serviceURL + "/Validate?data=" + data + "&callbackHandler=?", handleResponse);
}
When I attempted to create a class to wrap this functionality in a separate .js include file, the callbacks don't fire. Here is the class code from the .js include file:
function serviceProxy(myServiceURL) {
this.serviceURL = myServiceURL;
this.validate = function(data, successCallback, failureCallback) {
$.getJSON(this.serviceURL + "/Validate?data=" + data + "&callbackHandler=?", function(result) {
if (result.Success) {
successCallback();
}
else {
failureCallback(result.ErrorMessage);
}
});
};
And here is the JavaScript that's written in script tags directly on the page to make the calls:
function handleSuccess() {
// do something
}
function handleFailure(message) {
// do something else
}
function validate(serviceURL, data) {
var proxy = new serviceProxy(serviceURL);
proxy.validate(data, handleSuccess, handleFailure);
}
When debugging, the getJSON call is executed, but the handleSuccess and handleFailure callbacks are never reached. Any advice would be greatly appreciated.
Thanks in advance,
Ben
Have you tried replacing:
if (result.Success) {
successCallback();
}
with:
if (result.Success) {
successCallback(result);
}

Categories