I am attempting to override a models save method and set an error callback. I am using a mixture of localStorage and server side data so in the event that the app can't connect to the server, I want to save the model to local storage. Here is my model code:
var Project = Backbone.Model.extend({
urlRoot: Settings.urls.projects.project,
save: function(attributes, options){
options || (options = {});
this.set("last_updated", new Date().toISOString(), {silent: true});
options.error = function(){
console.log("Error callback");
}
return this.constructor.__super__.save.apply(this, arguments);
},
As you can see, I am attempting to set options.error within the save method and then call the super method to actually action the save. For some reason it is ignoring the function and the console log statement is not getting called. Anyone have any ideas?
Check this reference:
http://backbonejs.org/#Model-extend
You need to do something like this:
return Backbone.Model.prototype.save.call(this, attributes, options);
Related
Looking for some guidance on properly implementing custom logic on a model ("foo") using backbone.
We have a general requirement to avoid invocation direct .ajax calls (see below), and try to do everything consistently through the Backbone.js MV* framework.
I have implemented a method on the view (for lack of better place to put it), but I feel like this utility method would better be suited as a method on the model.
I've read articles suggesting implementation of over-riding the backbone .sync method to add custom methods on a model, but the trouble is, that I'm not sure it's "correct" to do so, when the model is expecting an object, the REST service method I'm actually invoking returns a simple boolean (true/false) value.
Here is the code I have implemented:
var myView = Backbone.View.extend({
initialize: function (options) {
...
},
canDelete: function (parmOne, parmTWo){
var okToDelete;
var url = '/url/to/check/if/delete/is/ok/parmOne/ParmTwo';
$.ajax({
async: false,
type: "GET",
url: url,
success: function (valBool, response, jqXHR) {
okToDelete = valBool;
},
error: function (data, textStatus, jqXHR) {
notifyError(data.responseText)
}
});
return okToDelete;
}
});
Looking for suggestions on implementation to make this cleaner?
Thanks
You are right about not overwriting Backbone.sync. As per the docs:
By default, it uses jQuery.ajax to make a RESTful JSON request and returns a jqXHR. You can override it in order to use a different persistence strategy, such as WebSockets, XML transport, or Local Storage.
You'd override sync if you want to define a different peristence strategy.
Model-specific utility functions belong in the model
For a utility function like yours the correct place to implement it would be on the model.
var myModel = Backbone.Model.extend({
canDelete: function (parmOne, parmTWo){
var url = '/url/to/check/if/delete/is/ok/parmOne/ParmTwo';
return $.ajax({
async: false,
type: "GET",
url: url
});
}
});
In your view, you'd probably have an even bound to a delete function, say, deleteModel. Since we wired the model.canDelete method to return the $.ajax promise we can do the error checking in the view, like this:
var myView = = Backbone.View.extend({
initialize: function (options) {
...
},
deleteModel: function(event) {
var that = this;
this.model.canDelete()
.done(function (data) {
// Do some stuff if ok to delete like:
that.model.destroy();
})
.fail(function (data) {
// Do some stuff if not ok to delete like:
that.model.notifyError(data.responseText)
});
}
});
Of course you can keep your success/fail callbacks like you had them, if the model is only going to be used in a very limited manner.
I can't seem to figure out which event to listen to when fetching data for a model. Usually when I'm doing it for a collection, I listen to the sync event. However, it seems like that doesn't work for models.
So, how do I know when my model is done fetching? Which event does it trigger?
Edit: Here's the beginning part of my view that is using the model:
var HomeContent = BaseView.extend({
initialize: function(options) {
self = this;
this.academyID = this.options.parent.academyID;
this.model = new AcademyModel({academyID: this.academyID});
this.model.on('sync', function() {
console.log('sync');
});
this.model.fetch();
}
fetch returns a jQuery promise. Just use something like:
this.model.fetch().done(function() {
...
}
Another solution is in the docs:
Accepts success and error callbacks in the options hash, which are both passed (model,response, options) as arguments.
There's a new behaviour in the latest version of Backbone (1.0.0 in which the reset event is no longer triggered by default after fetching a Collection.
http://backbonejs.org/#changelog
Renamed Collection's "update" to set, for parallelism with the similar
model.set(), and contrast with reset. It's now the default updating
mechanism after a fetch. If you'd like to continue using "reset", pass
{reset: true}.
The problem is that I want to capture the event when the collection has been finally fetched (pretty common case, indeed!)
I could listen to add, remove and change event, but if the collection is empty I don't know how to catch the event.
So, what would be the new, recommended way to catch when the collection request has finalized, or is it passing a { reset = true } the only way to achieve it???
ps: here's the original question, BTW can't catch Backbone Collection reset event
From Backbone.sync doc,
Whenever a model or collection begins a sync with the server, a
"request" event is emitted. If the request completes successfully
you'll get a "sync" event, and an "error" event if not.
For example,
var C = Backbone.Collection.extend({
url: '/echo/json/'
});
var c = new C();
c.on('sync', function() {
console.log('sync');
});
c.fetch();
And a demo http://jsfiddle.net/nikoshr/GLATm/
We can pass a method as a success handler when we call fetch on collection and as you said you just want to do something when everything[add,remove,update or reset] has happened, you can do inside this success handler.
collection.fetch({
success: function() {
// Do Something
// This is called when all add, remove and update operations have been done
}
});
Note: success handler is always executed irrespective of you have passed reset:true or not. Irrespective of your collection gets empty or not and It will be called at the last step when all the add,remove and update events have occurred.
Let me know if it does not solve your problem.
My own solution is indeed pretty simple. I already have a BaseCollection with added features, so in there I just set as default { reset: true }. The code should be something like this (my own BaseCollection has a lot of stuff that is not pertinent here):
var BaseCollection = Backbone.Collection.extend({
fetch: function(options) {
options = options || {};
options.reset = (options.reset === undefined ? true : options.reset);
// just call super.fetch
return Backbone.Collection.fetch.call(this, options);
};
});
Using promises...
// you could use promises as well
// P.S.: pardon jquery promises :)
var C = Backbone.Collection.extend({
url: '/echo/json/'
});
var c = new C();
// c.fetch() returns jqXHR object that you can listen too
$.when( c.fetch() )
.done(successFn)
.fail(failFn)
.always(alwaysFn);
I have a collection: conditions. In a view, after returning an HTTP 403 error response, I want to obviously NOT create the model:
var attributes = ...;
conditions.create(attributes, {
error: function (model, response) {
conditions.trigger('error');
var response = JSON.parse(response.responseText);
console.log(response);
}
});
The error callback is being called correctly. The response is logged correctly. But backbone still adds the (broken) model to the collection! When I look at conditions.toJSON(), there is a new model with some broken attributes.
I returned an error from the server, how do I insist that Backbone not add the new model to the collection?
I can do conditions.remove(model) in the callback, but should I have to?
You can pass {wait: true} to the create method.
Creating a model will cause an immediate "add" event to be triggered on the collection, as well as a "sync" event, once the model has been successfully created on the server. Pass {wait: true} if you'd like to wait for the server before adding the new model to the collection.
http://documentcloud.github.com/backbone/#Collection-create
So, within one of my views, I've got this function:
delete_model: function() {
var answer = confirm("Are you sure you want to delete this element?");
if (answer) {
this.model.destroy({
success: function() {
console.log("delete was a success");
}
});
}
});
When I ping that, the Ajax call goes out, the backend properly deletes the model and returns a 200 header with "OK" as the body...but the success event never fires. Am I missing something? What should I have the backend serve to fire that event?
I just had the same problem. The solution that worked for me was to make sure I return a json model from the server that corresponds to the deleted one.
edit: returning an empty json response will suffice.
Did not work:
delete(model) {
// deleted model from db
return "Model was deleted";
}
This did work:
delete(model) {
// deleted model from db
return model;
}
or:
delete(id) {
// deleted model from db with this id
return new Model {id: id};
}
Had the same problem using Backbone 1.5.3 with Rails. I tried rudena's solution, and it works!
Here's what my controller's delete function looked like initially:
def destroy
#cell = current_user.cells.find(params[:id])
#cell.destroy
render :json => "success"
end
And here's what worked:
def destroy
#cell = current_user.cells.find(params[:id])
#cell.destroy
render :json => #cell
end
That looks good to me, exactly what I have everywhere (except I have function(model) but that shouldn't matter at all) I do know that older versions of backbone didn't use the destroy(options) but instead had destroy(success, failure). Can you make sure you have the latest version.
Had this problem come up with my UI as well. Upon DELETE, the API came back with an empty 200 response.
What's happening is, jQuery expects a json response body, but when the response comes back empty, json parsing fails and the error callback is triggered.
My solution was to override the Model's sync method:
var MyModel = Backbone.Model.extend({
// Fix for empty DELETE response
sync: function(method, model, options) {
if (method === 'delete') {
options.dataType = 'html';
}
Backbone.sync.call(this, method, model, options);
}
});
This works because options is passed to jQuery's ajax call and we're instructing jQuery not to expect json.