Backbone not sending PUT to server - backbone.js

I am having trouble with trying to get backbone to send a PUT request out to my rails server on save. I am not sure what I am doing wrong here, it is fine with GET requests just not PUT..
Here is the code that is in my view that I use to save my model.
e.preventDefault()
$(#el).find('#error_explanation').html ""
data = Backbone.Syphon.serialize(this)
setError = false
#model.set(data, error: (model, error) ->
setError = true
)
#model.save()
Any help would be greatly appreciated.
Thanks,
-do

Stupid me....
Here is what was in my model.
validate: ->
console.log "validating"
console.log "options: #{options}"
errors = []
if(!#.validateEmail(#.get('email')))
errors.push("Email can't be blank")
if(!#.validateName(#.get('first_name')))
errors.push("First name can't be blank")
if(!#.validateName(#.get('last_name')))
errors.push("Last name can't be blank")
return errors
This was actually being called when I was trying to save it which I was just trying to call manually. Since I wasn't returning the correct data saying that it was valid/invalid it just was always invalid.
ooops...
-do

Related

Issues with single-requests in Restangular

I'm having a slight issue with my ability to consume REST data retrieved via Restangular in an angular controller. I have the following code which works fine for a list of accounts:
var baseAccounts = Restangular.all('accounts');
baseAccounts.getList().then(function(accounts) {
$scope.accounts = accounts;
});
This works perfectly for a list. I use similar syntax for a single account:
var baseAccount = Restangular.one('accounts');
baseAccount.getList(GUID).then(function(returnedAccount) {
$scope.currentAccount = returnedAccount;
});
I am using ng-repeat as the handling directive for my first request. I am attempting to bind with {{ account.name }} tags for the single request, but it does not seem to display any data despite the request being made properly. GUID is the parameter I must pass in to retrieve the relevant record.
I have combed through Restangular docs and it seems to me like I am composing my request properly. Any insight would be greatly appreciated.
EDIT: I've tried all of the solutions listed here to no avail. It would seem Restangular is submitting the correctly structured request, but when it returns it through my controller it shows up as just a request for a list of accounts. When the response is logged, it shows the same response as would be expected for a list of accounts. I do not believe this is a scoping issue as I have encapsulated my request in a way that should work to mitigate that. So, there seems to be a disconnect between Request -> Restangular object/promise that populates the request -> data-binding to the request. Restangular alternates between returning the array of accounts or undefined.
Have you looked at:
https://github.com/mgonto/restangular#using-values-directly-in-templates
Since Angular 1.2, Promise unwrapping in templates has been disabled by default and will be deprecated soon.
Try:
$scope.accounts = baseAccounts.getList().$object;
try:
var baseAccount = Restangular.one('accounts', GUID);
baseAccount.get().then(function(returnedAccount) {
$scope.currentAccount = returnedAccount;
});
The problem here is that it's expecting an array to be returned. I'm assuming that you are expecting an account object. Thus we need to use the get function, intead of getList()
The one() function has a second argument that accepts an id e.g. .one('users', 1). You can take a use of it.
CODE
var baseAccount = Restangular.one('accounts', 1); //1 would be account id
baseAccount.getList('account').then(function(returnedAccount) {
$scope.currentAccount = returnedAccount;
});
OR
var baseAccount = Restangular.one('accounts', 1); //1 would be account id
baseAccount.all('account').getList().then(function(returnedAccount) {
$scope.currentAccount = returnedAccount;
});
For more info take look at github issue
Hope this could help you, Thanks.

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...

Qunit + Sinon to test Backbone's model events

These days I'm trying to put up some tests for my first serious Backbone app. I had no problem so far with normal test but now I'm stuck trying to setting up an async test.
Basically my server API return a page with a 500 HTTP code error if I try to save a model with invalid attributes and I want to check if this trigger the right "error" state in Backbone.
I've tried to set-up the test in this way:
asyncTest("Test save Model function", function(){
expect(1);
var user = new User({});
var err_spy = this.spy();
user.on('error',err_spy);
user.save(user,{error:function(){
start();
equal( err_spy.callCount, 1, "Callback 'error' called once");
}});
});
The problem is that the error callback of the save function overrides the one in the model, so the only way to trigger it would be to do it manually:
user.trigger("error");
I don't think it is a right way to test because in my production environment there is no error callback for model's save function, but on the other hand I don't know how to tell Qunit to wait the ajax response to evaluate the test assertion.
Can someone suggest me a way to make it work? Thank you!
Something like this should do the trick. I'm going from memory here, but the sinon fake server should allow you to immediately return the 500 error state and subsequently invoke the spied-on function. You might need to tweak the server.respondWith(...) call.
asyncTest("Test save Model function", function(){
expect(1);
var user = new User({});
// Set up a fake 500 response.
var server = sinon.fakeServer.create();
server.respondWith(500, {}, "");
// Create the error callback.
var err_callback = function(){};
var err_spy = sinon.spy(err_callback);
user.save(user, {error: err_callback});
server.respond();
equal( err_spy.callCount, 1, "Callback 'error' called once");
server.restore();
});

CakePHP redirecting on error

I'm receiving a strange error from my call to query() the database. This normally wouldn't be a problem, except that it seems that the query call is redirecting me to the 500 server error page and not executing the rest of my code.
Regardless of the error, this PHP is executed during an AJAX call, meaning that I'm unable to return a properly formatted json string containing the error because CakePHP is redirecting.
A workaround would be to just parse the big HTML response from the server if there is an error, and use javascript to figure out what went wrong and display it properly, but I would much rather return a little json string from the backend that plays nicely with the code I already have in place.
Here is my call:
$this->FileUpload->query('exec sproc_runjob_ProcessTests', false);
Any help would be much appreciated. Thanks in advance!
As bfavaretto mentions above, the correct answer is to surround the query line in a try catch.
Here is the code I used:
try{
$this->dbParent->FileUpload->query('exec sproc_runjob_ProcessTests', false);
} catch(PDOException $e){
if($e['errorInfo'][0] == 42000 && $e['errorInfo'][1] == 22022)
return array('error' => 'sproc_blocked');
}
return array('success'=>true);

Error callback always fired, even when it is successful

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.

Resources