Backbone.js Backbone.wrapError function - ajax

This questions was about Backbone 0.9.2
Since upgrading to Backbone 0.9.10 we've chosen to override Backbone.sync instead and it works like a charm.
December 2012 - (v0.9.9) Backbone.wrapError has been removed.
February 2013 - It looks like WrapError will be brought back in the next version of Backbone, but overriding backbone.sync is the way to go.
(sorry for the long read)
I'm modifying the Backbone.wrapError function and I'm baffling by a line. I know what the line does, but not WHY it is necessary.
resp = model === originalModel ? resp : model;
resp ends up being the textStatus/errorType ie: "error" "timeout"
"parse error"
model is the XHR request object
originalModel is a reference to the Backbone.Model instance which ultimately called this function
I have a good grasp on what Backbone.wrapError does, what it returns and how it is used but I can't seem to understand the purpose of the above line.
Backbone's documentation states that wrapError will 'Wrap an optional error callback with a fallback error event,' which is true. Additionally, I've learned is that Backbone.wrapError is called 4 times in the library from within the fetch, save, destroy and reset functions in order to ensure that AJAX errors do not go unnoticed by the library. For example, if an AJAX error callback is passed into the fetch method it will be executed with a few parameters passed along, if not, the model will trigger an error event with the same few parameters passed along.
Sample call:
options.error = Backbone.wrapError(options.error, model, options);
Backbone.wrapError:
Backbone.wrapError = function(onError, originalModel, options) {
return function(model, resp) {
resp = model === originalModel ? resp : model;
if (onError) {
onError(originalModel, resp, options);
} else {
originalModel.trigger('error', originalModel, resp, options);
}
};
};
The problem that arises with this line (resp = model === originalModel ? resp : model;) is that model and resp correspond to the first 2 parameters within the jQuery/Zepto error callback parameter list. The first problem I have is with the naming of these parameters (model, response), because while debugging I've seen that those 2 parameters are jqXHR/xhr and textStatus/errorType. The textStatus/errorType parameters usually end up being "error" but (according to the docs) can also be "timeout" "parse error" etc. The comparison of model === originalModel makes no sense to me. A hard comparison on an XHR object and a Backbone.Model instance will always fail, and model will be stored into response (resp), which is fine because the model is actually the XHR response object... this line just seems pointless to me, but I went ahead and included it in my modified wrapError method.
Because model === originalModel always evaluates to false, the line seems to be an elaborate version of resp = model; which is useless, because you could just remove the line entirely and the model parameter could be passed into originalModel.trigger('error', originalModel, resp, options); instead of resp.
Is there any instance where model === originalModel could possibly evaluate to true?
Anybody with more experience in Backbone.js, AJAX have an answer/explanation of why this line is necessary?

TLDR/CLIFFS:
The weird little line below is used to determine weather the error callback was triggered from a failed validation at the model level, or from a failed AJAX call from the fetch, save, or destroy methods (which all call Backbone.sync). If the failure is from validation, it does not change the resp variable because resp should already hold useful information returned by validate (such as an errors array or a string about the error). If the failure is from a bad AJAX request, the XHR object is stored into resp because the XHR is the most informative item available. Unfortunately, the XHR is passed into this function as model and Backbone documentation fails to point out that this parameter does not always represent a model. Resp is meant to hold useful response information about the error(s), and is sent to the error callback or a thrown error event.
Okay. I learned some things about this weird line.
resp = model === originalModel ? resp : model;
In Backbone there are AJAX errors and Validation errors. Conveniently, Backbone funnels both errors into the same function -- the AJAX error callback. The problem is that the arguments passed into these functions are inconsistent. When there is an AJAX error an XHR object is available, but not during a validation error.
If there is no callback present, Backbone will throw and 'error' event with the same parameters that would have been passed into the error callback. (line 7 and 9 below).
After a successful AJAX request, your JSON data can be optionally passed through the model's validate function. In Backbone, the validate function should return false or nothing at all when there are no errors. When there ARE errors, it is typical to return an array such as ['invalid username', 'password too long', 'etc...'] Anything returned from validate (usually an error messages array) is passed into the 'wrapped' error callback as the resp parameter and the model itself is passed as model!
The _validate function is a bit sloppy and has multiple return statements, but when validation fails, line 9 is hit. Line 9 of the _validate function passes this (the model), error (returned from the models validate method), options (ajax options, success, error, timeout etc). This differs from an AJAX error which will pass in xhr (xmlhttprequest object), errorType ('error' 'timeout' 'parse error' etc), options (ajax options).
validate error: error(model, validate_return_value, options)
ajax error: error(xhr, errorType, options)
1 _validate: function(attrs, options) {
2 if (options.silent || !this.validate) return true;
3 attrs = _.extend({}, this.attributes, attrs);
4 var error = this.validate(attrs, options);
5 if (!error) return true;
6 if (options && options.error) {
7* options.error(this, error, options);
8 } else {
9 this.trigger('error', this, error, options);
10 }
11 return false;
12 }
The strange line of code above is necessary because, this one function handles errors from 2 different methods. AJAX and Validation. Those 2 send it different parameters, so this is meant to normalize them and throw events with consistent parameter lists.
When a validation error occurs, the model does not change so the model that is passed into the error callback is exactly equal to the originalModel. The purpose of the resp parameter is to hold information about the error itself. When there's an AJAX error, 'timeout' 'parse error' or 'error' are simply not as informative as the XHR object.
That weird little line determines weather the error callback was accessed from _validate or through a normal AJAX error such as a 404. When it is accessed from validate, resp is the value returned from validate. It should be informative, and useful data for the front-end templates to display. When the resulting error is from a HTTP error, the most useful information about that error is the XHR object which is passed into this function as the MODEL parameter.
A hopefully simplified approach to the wrapError and validate functions
Backbone.wrapError = function(ajax_error_callback, model_or_xhr, ajax_options) {
return function(model_or_xhr, error_info) {
if there was an ajax error, error_info = the xhr object
if there was a validation error, error_info = whatever was returned from validate
if there's an error callback {
run the error callback with (the original model, error_info, ajax_options) as parameters
if there is not an error callback
throw an event called 'error' with (the original model, error_info, ajax_options) as parameters
}
};
};
original:
Backbone.wrapError = function(onError, originalModel, options) {
return function(model, resp) {
resp = model === originalModel ? resp : model;
if (onError) {
* onError(originalModel, resp, options);
} else {
originalModel.trigger('error', originalModel, resp, options);
}
};
};
The * shows that error callback called from here

Related

Cypress - Having trouble asserting on requests using cy.intercept. 'cy.its() errored because the property: request does not exist on your subject.'

I'm trying to assert on the request body to ensure the correct new test card is passed as part of the order.
it("User clicks confirm & pay button to complete order", () => {
cy.intercept("/api/checkout/payandcommit").as("placeOrder");
cy.placeOrderAndPay();
cy.wait("#placeOrder")
.its("response.statusCode")
.should("eq", 200)
.its("request.body")
.should("include", "cardNumber", 370000000000002);
});
All is good upto verfying the status code then it breaks.
This is the error thrown:
Timed out retrying: cy.its() errored because the property: request does not exist on your subject.
cy.its() waited for the specified property request to exist, but it never did.
If you do not expect the property request to exist, then add an assertion such as:
cy.wrap({ foo: 'bar' }).its('quux').should('not.exist')
If i comment out the status code assertion this new error is thrown: object tested must be an array, a map, an object, a set, a string, or a weakset, but object given.
Any help getting this working would be much appreciated!
Chaining the assertions like this doesn't work, because the subject changes inside the chain
cy.wait("#placeOrder") // yields interception object
.its("response.statusCode") // yields number 200
.should("eq", 200) // yields it's input (number 200)
.its("request.body") // not testing the interception object here
.should("include", "cardNumber", 370000000000002);
One way that works is to use a callback which gets the interception object
cy.wait('#placeOrder').then(interception => {
console.log(interception); // take a look at the properties
cy.wrap(interception.response.statusCode).should('eq', 404);
cy.wrap(interception.request.body)
.should("include", "cardNumber", 370000000000002) // not sure this should is correct
.should("have.property", "cardNumber", 370000000000002) // maybe this is better
})
You may also be able to use chained commands if the subject is maintained, which means you have to tweak the should() in the middle
cy.wait("#placeOrder")
.should('have.property', 'response.statusCode', 200)
.should('have.property', 'request.body.cardNumber', 370000000000002);
Check out the logged interception object to make sure you have the correct properties and property value types (e.g is cardNumber a number or a string?).
Thank you so much for your help Hiram K! I was able to get it working with:
cy.wait("#placeOrder").then((interception) => {
console.log(interception);
cy.wrap(interception.response.statusCode).should("eq", 200);
cy.wrap(interception.request.body.paymentDetails[0].cardNumber).should(
"include",
"370000000000002"
);
cy.wrap(
interception.request.body.paymentDetails[0].defaultPayment
).should("eq", false);
});

Does promise object always need callback function?

I have a requirement that I need to create a service in Angular 1 and load data with an API call which can be accessed in controllers. I earlier tried by making a API call with $http service and assigning the data to the variable / object. I then injected the service and assigned the variable / object to the controller scope variable / object.
What I observed in the controller event loop is not same as service event loop and controller scope variable / object remains undefined. Later I got a solution to work by returning a promise from the service, and calling the promise in the controller, however I'm new to promises and not able to fully absorb that when I called a promise, I had to pass the function as argument which I think is a callback for the $http API call, but I'm uncertain how it's working under the hood. Can someone explain it?
//service code
this.getuserimages = function(uname) {
console.log("Username in UserImage Service: " + uname);
var promise = $http.get('/api/images/' + uname).then(function(response) {
this.userimages = response.data;
return this.userimages;
});
return promise;
}
// controller code
var userimagespromise = userImageService.getuserimages($routeParams.username);
userimagespromise.then(function(data) {
$scope.userimages = data;
Your code is a Promise chain.
I rewrited your code in a way that this chain is more clear, but the meaning is exactly the same:
$http.get('/api/images/' + uname)
.then(function(response) {
this.userimages = response.data;
return this.userimages;
})
.then(function(images) {
$scope.userimages = images;
});
You can read this flow in this way:
Please get me user images
And then, we they will be available (=> returned from the get and passed to the then function), save them in a variable and return them to the next element of the chain
And then, we they will be available (=> return from the previous promise), set them in the $scope
Please note that the entire flow is asynchronous because every Promise is "an object representing the eventual completion or failure of an asynchronous operation".
You can find more information in the Promise documentation.

CasperJS' sendAJAX asynchronous flag set to true failed

I set up a CasperJS script to call my web service (running on localhost:9000).
The webservice containing parameter which is needed to be filled. Let's say an amount parameter, and my webservice saves amount data from its parameter. So I wrote my CasperJS script like this:
casper.then(function(){
val = this.evaluate(function(){
//do step #1 ajax request
var country_amount = 9;
var params = "amount="+country_amount;
var data = "amount="+country_amount;
var wsurl = "http://localhost:9000/TempCountryAmountREST/setCountryAmount?amount="+country_amount;
//window.__utils__.echo("Country Amount :"+country_amount);
return JSON.parse(__utils__.sendAJAX(wsurl, "POST" , null, false, { contentType: "application/json" }));
});
});
As you can see, in the fourth parameter of __utils__.sendAJAX, I set it up with false, which means asynchronous = false. Well, everything goes well with async = false. That val variable successfully returning json data.
But when I changed false to true, it's coming up with a weird thing. Sometimes it succeeds to save the data (with my webservice), but val doesn't return proper value (it's null, but it should be returning json data). But saving data is still successful when I see on my phpmyadmin. But sometimes too (almost always happened), it fails to save the amount data, and still, returning null (not json data)
So what happened with this, is there a problem with use async request in CasperJS sendAJAX?
That is the expected behavior when an execution is asynchronous. Take for example the following code which is asynchronous by using the setTimeout function:
function sendAsync(){
var returnVal;
setTimeout(function(){
returnVal = sendSync();
}, 100);
return returnVal;
}
Since setTimeout is asynchronous sendSync is called or even finished after sendAsync finishes. You simply cannot return something based on an asynchronous call. See this question for more information.
__utils__.sendAJAX() doesn't provide a callback which could be used to get the returned data asynchronously. Since you want the data to be returned, you have to set the asynchronous argument to false.
__utils__.sendAJAX() with the asynchronous argument set to true can only be used for so called fire-and-forget requests where you are not interested in the data returned. Usually you're interested in the returned data.
The reason it sometimes fails to properly send and save the data is probably because this is an asynchronous process and your CasperJS script exits before the request is completed, thus terminating your request. By specifying async as true, you essentially make a tiny break off from the control flow.

why doesn't ajax fn() follow the code sequence

I just wanted to understand the coding flow of ajax function. let me give one simple example,
function test() { alert("Hello"); alert("World"); }
if you run this function, the first dialog would be Hello and the second would be World, Right? But why in ajax the coding structure doesn't go through in same fashion.
ajaxRequest.open("POST", "pos/write", true);
ajaxRequest.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
ajaxRequest.send("user=Ram"); /* this line can be written anywhere */
ajaxRequest.onreadystatechange = function() {
if(ajaxRequest.readyState == 4 && ajaxRequest.status == 200){
alert("done"); }
// ajaxRequest.send("user=Ram");
as you can see, .send('user=Ram') written above & below the .readyState, but it runs successfully in both case. This is what i want to understand, why doesn't ajax fn() follow the code sequence
You have to keep in mind that .send() is asynchronous. Immediately after .send() is called, you're wiring up the onreadystatechange handler on the same object that will eventually receive the response from pos/write. Even though it appears after the request was initiated, there will always be time for it to run and wire that handler up before the response comes back, the XHR object changes its readyState to 4, and your onreadystatechange event handler is executed.

Significance of Reflection in AJAX-Based Applications

Ajax and Reflection
I am developing an ajax-based application and wondering, what role reflection plays or might play here?
Probably most importantly I am asking myself, if it would be a good approach to
handle all ajax responses through a single handler,
reflect or interpret the data or error
delegate further processing (e.g. where to inject the html) based upon the analysis.
Is this a budding procedure? What pros and cons come to mind?
Additional clearification
My current implementation, which I am not happy with, looks like this.
Register eventhandlers for user action, which lead to ajax requests.
For each request:
Determine which container is the target for the new content
Validate the ajax response
Pass the result to the appropiate rendering function if everything is as expected
Here is an example
function setGamedayScoringChangeHandlers() {
$("#community").delegate("div.community div.nav", "click", function() {
var orderId = $(this).html();
var communityId = $(this).closest('.communityView ').dashId();
requestGamedayScoringByOrderId(communityId, orderId);
});
}
function requestGamedayScoringByOrderId(communityId, orderId) {
var $targetContainer = $('#community-' + communityId + '-gameday');
$.ajax({
url: '?api=league&func=getGamedayScoringByCommunityIdAndOrderId',
data: {
communityId : communityId,
orderId : orderId
},
success: function(result) {
// custom indicator, that sth. didn't work as supposed
if (result.success === false) {
// a php error couldn't be handled as expected
if (result.error === 'phpRuntimeError') {
// ..
}
// ..
}
else {
renderGamedayScoring(result, $targetContainer);
}
}
});
}
Question
How can this and especially the redundant error checking be simplified? Could Reflection, in a sense of: "Is the response valid? And what does the error message say or data look like?" be a reasonable structure do deal with this? Additionally: Is the "coupling" of the actual ajax request and determing the $targetContainer a "normal" procedure?
Many thanks,
Robson
Yes I think register ajax handler trought one pipe is a good way, because it is more easy to control, you will have less redundant code and less boarding effects. If I look at your code comments it seems the response is not as you expect. I use to do like this for controling a group of ajax request talking with server script. I build one request object like :
// myscript.js
var rqPHP = {
url:'php/dispatcher.php', type:'POST', dataType:'json',
success:function(json, status, jXHR){
//console.log('rqPHP.succes : ', json);
if(!json) return console.warn('[rqPHP.success] json is null');
if(!json.cmd) return console.warn('[rqPHP.success] json.cmd is null');
if(!json.res) return console.warn('[rqPHP.success] json.res is null');
if(json.err && json.err.length){ console.warn('[rqPHP.success errors cmd:'+json.cmd+'] '+json.err);}
// so if no errors, dispatch actions based on original command asked
switch(json.cmd){
case 'loadfile' :
// do whatever with response
break;
case 'savefile' :
// do whatever with response
break;
}
},
error:function(jXHR, status, err){
console.warn('[rqPHP.error] ', status,',',err,',',jXHR.responseText);
}
};
then when use this object trought all my group of different actions and I precise wich action and arguments I pass. I use to ask for a json data so I am able to receive an easy parsing response, so I am able to return the original command asked, and some details on errors that may occured for example, and when I need to fire the request :
// myscript.js
rqPHP.data = {'cmd':'loadfile', 'filename':'file.dat', 'arg2':'other argument'};
$.ajax(rqPHP);
Then an example of one server script that will respond :
// dispatcher.php
$pv = $_POST;
$res = '';
$err = array();
// you check the command asked for :
switch(strtolower($pv['cmd'])){
case 'savefile' :
// do whatever
break;
case 'loadfile' :
// do whatever
if(any error){
$err[] = $loadError;// push error with whatever details you'll retrieve in javascript
}else{
$res = ',"res":"'.$dataLoaded.'"';// format json response so you'll check the var exist
}
break;
}
$jsonRes = '{"cmd":"'.$pv['cmd'].'"'.$res.',"err":"'.implode('|', $err).'"}';// json result
print $jsonRes;
They may be some errors, it is just for the principe, I hope that will help, just some last advices :
you should better use the requestObject.data to pass any arguments instead of setting the url like you did, this is much more easy because jQuery does the properly encoding work
you may use POST so the url stay clean, post vars are 'hidden'
in your case, because you may want to centralize server actions with ONE server script, you should use 'json' as dataType because it is much easier to retrieve details from the response, such errors. You have to distinct the ajax error that is trigger when the url doesn't exist, or access denied, well when the server replies it just can't respond to this request, and distinct the properly response of your server script, I mean the script responds well but it may occur an command error, for example for a 'loadfile' command, the argument fileUrl may be wrong or unreadable, so the action is done but the response will be not valid for you...
If you plan to fire many loads for differents parts (I mean you may don't wait response for an ajax before loading a new one), it should be better to set main success and errors functions for keeping centralization and then build one new request object each time you make a load
function rqSuccess(json, status, jXHR){
// put same checking code as before, then you can also retrieve some particular variables
// here, 'this' should correspond to the request object used for the $.ajax so :
console.log('myTarget is : ', this.myTarget, ' , myVariable is : ', this.myVariable);
}
function rqError(jXHR, status, err){
// put same checking code
}
// then each time you want make one or many independant calls, build a new request object
var myRq = {url:'dispatcher.php',type:'POST',dataType:'json',
success:rqSuccess,
error:rqError,
myTarget:$('#myblock'),// any variable you want to retrieve in response functions
myVariable:'Hello !',// after all it is an object, you can store anything you may need, just be carefull of reserved variables of the ajax object (see jQuery $.ajax doc)
// the data object is sanitized and sended to your server script, so put only variables it will need
data : {'cmd':'loadfile',...}
}
$.ajax(myRq);
// you may load an other independant one without waiting for the response of the first
var myRq2 = {...myTarget:$('#anotherblock'), data:{'cmd':'anotheraction'}...}
$.ajax(myRq2);
As a first step, you should change the error handling on the serverside to produce a non-OK/200 response for error cases, e.g. throw a 500. Then have that handled as an actual error on the clientside, along with other errors, instead of putting it through the success-callback.
That way you can use jQuery's abstractions for global error handling: http://api.jquery.com/ajaxError

Resources