Error callback always fired, even when it is successful - backbone.js

There should be a glitch in my syntax, or something I haven't understood, but when I am doing a save method on my model. The error callback is called whatever the outcome of the method.
#new_activity = new Activity()
#new_activity.save
know: $('input#know').val()
learn: $('input#learn').val()
success: -> console.log 'success'
error: -> console.log 'error'
In my case, since I do not really know whether the new_activity has effectively passed the validation, I have to do an ugly trick to add the activity to the collection. (By the way, I do not use the create method since I do want to have the different errors, and not a simple "false".
if #new_activity.has('know') and #new_activity.has('learn')
app.collections.activities.add #new_activity
When it is successful though; there is an alert of the created model.
Edit: Further details.
Here is my model:
initialize: ->
_.bindAll #, 'validate', 'errorHandler'
#.bind 'error', #errorHandler
validate: (attrs) ->
errors = []
# We only support a certain number of languages, we enforce that the user does not select a wrong set.
if _.isEmpty(_.intersection([attrs.know], ['en', 'fr'])) is true
errors.push 'This language is not currently supported.'
if _.isEmpty(_.intersection([attrs.learn], ['en', 'fr', 'de', 'es', 'zh', 'pt', 'ar', 'ja', 'ru'])) is true
errors.push 'You cannot learn this language yet.'
if _.isEmpty(errors) is false
errors
errorHandler: (model, error) ->
console.log error
When the validation occurs, and if the validate method returns nothing, it still triggers the error event, and the error variable contains the model (but no error message).

You should check whether the new_activity is saved properly, please verify that the server returns success response to the PUT request.
Furthermore, I have had issues with using Rails 3.1 standard format.json { head :ok } because it returns a single space as a reponse and application/json as content type. Backbone then tries to parse JSON and dies with an error.

The problem is the single space that Rails sends back with head :ok. If you send back an empty string, Backbone handles it fine.
The proper way to do that is to send back a 204 No Content. You can do that by replacing head :ok with head :no_content in the format.json block. Rails will not send back anything in the HTTP body.

I'm not sure what the cause of your error is, but I think I can help you pin it down:
Looking at the annotated source for Backbone.js, you can see that save() defers to sync(), which in turn defers to jQuery's $.ajax. So ultimately, your error function (wrapped by Backbone's wrapError) is called from that.
Unfortunately, the wrapper discards all but the jqXHR argument, making debugging a bit tricky. So you might want to try hacking your local Backbone.js source file to replace the existing wrapErrorfunction with something like this:
var wrapError = function(onError, model, options) {
return function(jqXHR, textStatus, errorThrown) {
console.log(jqXHR);
console.log(textStatus);
console.log(errorThrown);
if (onError) {
onError(model, jqXHR, options);
} else {
model.trigger('error', model, jqXHR, options);
}
};
};
Then, at least, you'll see all of the debugging data provided by jQuery on your console.

Related

Callback registered with respond is not executed when path is matched

I am trying to test an AngularJS directive which uses an HTML template through a reference. Because of that, it is issuing an http request.
What I am trying to do is capturing all requests for the templates using httpBackend.whenGET("....").respond(function) to load that template from the local store.
The point is that the function is never called. I know it is matching the path because it shows no error saying the call was not expected.
By the way, I prefer to do it this way instead of using html2js.
Let's give an example.
If I set the backend.when wrong (non matching path) I get as error
Error: Unexpected request: GET resources/js/app/views/search/parts/con-search-filter/template.html
No more request expected
If I execute in my code (and I know whenGET gets called):
httpBackend.whenGET(/(.*)template.html/, undefined, undefined,
["basePath"]).respond(function(method, url, data, headers, params){
debugger;
console.log("hello");
return [200, []];
});
Then, the previous error is gone. The point is the breakpoint (debugger;) is not hit and "hello" is not printed.
I am importing angular-mocks v1.5.11 and, according to the documentation, in the line 1255, I should be able to do it
$httpBackend.whenPATCH(/\/user\/(.+)\/article\/(.+)/, undefined, undefined, ['user', 'article'])
.respond(function(method, url, data, headers, params) {
// for url of '/user/1234/article/567' params is {user: '1234', article: '567'}
});
I am using jasmine-maven-plugin version 2.2, with the chrome driver. I am executing the bdd goal.
I have figured it out what it was. Just, the requests were pending and I had to do an httpBackend.flush().

Validation doesn't work?

I have some code:
var Person = new Backbone.Model({name: 'Jeremy'});
Person.validate = function(attrs) {
if (!attrs.name) {
return 'I need your name';
}
};
Person.on("invalid", function(model, error) {
alert(model.get("title") + " " + error);
});
Person.set({name: 'Samuel'});
console.log(Person.get('name'));
// 'Samuel'
Person.unset('name', {validate: true});
console.log(Person.get('name'));//Why can i print name here if it unsetted?
When i type unset method i see an error alert. It's correct. But why can i print in console the name if it was unsetted?
The name is still there because the validation failure stopped the unset from doing anything.
The documentation isn't very explicit about how validation works with set and unset but it is quite explicit with save:
validate model.validate(attributes, options)
[...] If validate returns an error, save will not continue, and the model attributes will not be modified on the server.
So it is reasonable to think that validation errors will prevent the current operation (set, unset, save, ...) from changing anything.
You can see how it works by examining the Backbone source code. First, you need to know that unset is just a set call in disguise:
unset: function(attr, options) {
return this.set(attr, void 0, _.extend({}, options, {unset: true}));
}
So we look at set:
set: function(key, val, options) {
// A bunch of boring bookkeeping stuff...
// Run validation.
if (!this._validate(attrs, options)) return false;
// The stuff that changes attributes and triggers events.
}
The validations happen as soon as set knows what it is working with and set returns without changing anything if the validations fail.
The Backbone documentation leaves a lot of important things out so you need to be passingly familiar with the Backbone source if you're going to use Backbone. The source is fairly straight forward, don't be afraid to jump into it to see what's going on.
Do this: To not trigger events you can use the silent:true option. I believe there might be some issue upstream in your code. Anyways, do the following - it should work. ( in my tests, it did ).
Person.unset('name',{validate: true,silent:true})
p.s.: Mu ( below ) gives great information.

destroy always returns with the error callback (although everything seems to be ok)

I'm trying to delete a model on my backend and what I do is this (the code is adapted just to show you the issue, some parts could be missing):
attending= new Backbone.Model();
attending.url= this.url() + "/reject";
attending.set({
id: this.id
})
attending.destroy({
success: function(){
alert("yes");
},
error: function(){
alert("no");
}
});
but what I always obtain is a "no" alert. The fact is the backend seems to be updated correctly and what I obtain as a response too. Here it is:
so... what's wrong with the response I get? Why doesn't backbone recognizes it as a successful response? I get 200/OK and a "application/json" format as well!
Your backend should return something with 200
jQuery expect 200 with application/json to have some content
Have a look here: https://github.com/jashkenas/backbone/issues/2218#issuecomment-20991938
You might want to place a "debugger;" in the error callback and trace exactly why its coming that route vs the success route. That should atleast get your started on the right path...

BackboneJS model.fetch() unsuccessful

Hi I have this model :
window.shop = Backbone.Model.extend({
initialize: function () {
console.log('initializing shop');
},
urlRoot: "shopData.json",
});
and then i go :
var myShop = new shop();
myShop.fetch({
success: function (model, resp){
console.log(resp);
},
error: function (model, resp){
console.log("error retrieving model");
}}, {wait: true});
now I'm always getting the error message - never reaching success :-(
thanks for any help.
Edit 1:
As per your comment the server is sending the proper response but Backbone is still calling the error function. Add the following line at the beginning of the error callback:
error: function (model, resp){
console.log('error arguments: ', arguments);
console.log("error retrieving model");
}
The first line should print an array of objects. The first element in the array should the jqXhr object, the second should be a string representation of the error. If you click on the first object, the dev tools will let you inspect its properties. Read up on the properties of the object here http://api.jquery.com/jQuery.ajax/#jqXHR.
Using that information you can verify if the jQuery is receiving an error from the server.
If there is no server side error, then check the value of the responseText property. That holds the string data returned from the server. $.ajax will try to parse that data into JSON. Most likely the parsing is throwing an error and the error handler is being raised instead.
Copy the response text and paste it into http://jsonlint.com/. Verify that the response sent from the server is valid JSON. Do update your question with the output of the console.log statement and the responseText property of the jqxhr object.
-x-x-x-
You seem to be using the model independently. As the per the documentation, http://backbonejs.org/#Model-url,
Generates URLs of the form: "/[urlRoot]/id"
That means, you are making a request to shopData.json/id. Also, you haven't specified the id.
Insert a console.log(myShop.url()) before the myShop.fetch(). Let us know whats the output. Also, possibly share the details of the ajax request as seen in Firebug or Chrome Dev Tools. I am interested in two things, the request url and the response returned by the server. (http://getfirebug.com/network)

Backbone.js getting response from server after a save()

I have a mode that saves data to a codeigniter function the codeigniter function returns valid JSON data back if it has an error how do i get the error details that the server returns when using save(). I have the following code that doesn't work
this.newproject.save({
'Objective':Objective,
"Planner":Planner,
"NISupervisor":NISupervisor,
"SiteIDs":SiteIDs,
"Status":Status ,
"StartDate":StartDate,
"EndDate":EndDate,
"Details":Details,
"PrjTitle":PrjTitle
},{
success:function(model,response){
console.log(response);
}
},{
error:function(){
alert("wrong");
}
});
Success doesn't work at all
The 2nd option to save is an object with 2 properties, success and error. I'm assuming that you mean that "error" doesn't work at all, and success works fine, based on your actual question text.
this.newproject.save({
'Objective':Objective,
"Planner":Planner,
"NISupervisor":NISupervisor,
"SiteIDs":SiteIDs,
"Status":Status ,
"StartDate":StartDate,
"EndDate":EndDate,
"Details":Details,
"PrjTitle":PrjTitle
},{
success:function(model,response){console.log(response);},
error:function(model,response){console.log(response);}
});
The error callback also passes model and response, so the response argument is what you're probably looking for.
The problem with your code is that you've got three hash arguments. The save method accepts attrs and options as its two arguments. As such, your code should look similar to this:
var attrs = {
"Objective":Objective,
"Planner":Planner,
"NISupervisor":NISupervisor,
"SiteIDs":SiteIDs,
"Status":Status ,
"StartDate":StartDate,
"EndDate":EndDate,
"Details":Details,
"PrjTitle":PrjTitle
};
this.newproject.save(attrs, {
success: function(model, response) {
console.log(response);
},
error: function(model, response) {
alert('wrong');
}
});
So your call would not have been able to attach the error function. The above example should work for you since it combines the success and error functions in the second hash argument.

Resources