I thought that initializing the collection returned a ready instance of Backbone.Collection. After the fetch, however, the collection contains some models, but in require, _this.serviceCollection.toJSON() gives me undefined object.
My Backbone.Collection:
var ServiceCollection = Backbone.Collection.extend({
model: Backbone.Model,
initialize: function(options){
this.fetch({
url: 'service/' + options + '/',
async: false,
success: function(collection, response, options){
collection.set(response.result);
}
});
}
});
return ServiceCollection;
My CollectionView showing:
OpenServices: function(category){
var _this = this;
require([
'service/js/service_collection',
'service/js/service_collection_view'
], function(ServiceCollection, ServiceCollectionView){
_this.serviceCollection = new ServiceCollection(category);
_this.serviceCollectionView = new ServiceCollectionView({
collection: _this.serviceCollection
});
_this.categoryLayout.categoryItems.show(_this.serviceCollectionView);
});
}
What's wrong this code?
Mistake was in sync/async request. My fetch was async: false, so I'm just change it to async: true and a set event on reset collection for that collectionView.
I suspect the issue you are having is related to your success callback. There should be no need to call collection.set(...) to add the models to the collection; that is done automatically for you when the fetch succeed. In fact, the documentation around Collection.set provides this nugget:
... if the collection contains any models that aren't present in the list, they'll be removed.
I suspect that response.result in your success callback doesn't contain any of the data you think it does. Because of that, your call to collection.set(...) is actually removing all items from the collection.
Try removing that success callback & see what else happens.
Somewhat unrelated but still important:
Using Collection.fetch(...) synchronously is considered bad practice; if your server takes longer than a few hundred milliseconds to return the data, the browser may lock up.
Specifying the url as a parameter to Collection.fetch(...) is not a terrible idea, but consider extending Backbone.Model and specifying the urlRoot parameter instead. If your server follows REST-ful conventions, it makes it very easy to create/update/delete data that way.
Related
I have written the following code in my model:
urlroot: '/url/sms',
setAuthId: function(value) {
var _this = this ;
if (this.get("smsauth") != value) {
this.set("smsauth",value);
this.save();
//Ideally, I want to achieve this AJAX call request with backbone.
// $.ajax({
// url: "/url/sms",
// data: value,
// type: 'PUT',
// processData: false,
// success: function(result) {
// _this.set("authId", value);
// },
// error : function(){
// console.log('Error setting authid');
// }
// });
}
},
Ideally, we should be firing a "PUT" request everytime. But the backbone is firing a POST request because "ID" is not present.
I'm quite new to backbone, I was wondering if there is anyway to sync with server without having to pass an ID? How can I solve this problem?
I basically want to fire a PUT request NOT post request for the URL. (Since my backend only supports PUT request).
The only real way to force Backbone.Model.save() to do a PUT is the way #dbf explained, you have to set your idAttribute. To properly set idAttribute your model should have an attribute that is unique. (This is not a hard requirement, since model.isNew() just checks that your model has a property named id or whatever string you supply to your model idAttribute property. It doesn't check for uniqueness).
I sense that in your case, you may not have a unique attribute in your models, so setting idAttribute may be a challenge. For that reason, I suggest you don't specify an idAttribute in your model definition. Rather, we just handle it dynamically.Just refactor your code like this:
setAuthId: function(value) {
var _this = this ;
if (this.get("smsauth") != value) {
// any model attribute is fine, we just need to return a prop
this.prototype.idAttribute = "smsauth"
this.save("smsauth",value) // Save will do a set before the server request
// model.save returns a promise. Here we'll reset the idAttribute
.then(/* success handler */ function (response) {
_this.set("authId",value);
_this.prototype.idAttribute = 'id' // You can set this to "authId" if that
// uniquely identifies this model
},/* error handler */ function (response) {
_this.prototype.idAttribute = 'id' // reset idAttribute to default
});
}
}
It's not really clear to me what you are saving. The fact you are handling with POST suggest its a new entry. Quoting from the docs this behaviour is correct. PUT is update, POST is create (under CRUD operations).
If the model isNew, the save will be a "create" (HTTP POST), if the
model already exists on the server, the save will be an "update" (HTTP
PUT).
What you might try is what #Sami suggest by overwriting the save with an update request (do realise that all solutions here are incorrect/workarounds).
If you really need PUT and cannot alter your backend for some mysterious reason to accept POST, you could change the idAttribute within the model.
var smsModel = Backbone.Model.extend({
idAttribute: "smsauth" // for example
});
Do realise that your backend, very likely, has a design flaw where you are changing and creating workarounds to work with it, which you should consider to avoid.
I have used this snippet.
this.save({}, {
type: 'PUT'
});
I found all your answers really fascinating, I am going to try everyone of them.
Thanks for your suggestions, this is why I like SO. Something new to learn.
You can override save method.
Something like
var model = Backbone.Model.extend({
save: function(options){
return Backbone.sync("update", this, options);
}
});
Following Backbone/Marionette Controller and Collection won't fetch.
define(["jquery", "backbone","models/Poi"],
function($, Backbone, Poi) {
// Creates a new Backbone Poi class object
var PoiCollection = Backbone.Collection.extend({
model:Poi,
parse: function (response) {
console.log(response);
// Return people object which is the array from response
return response;
}
});
// Returns the Poi class
return PoiCollection;
}
);
define(['App', 'backbone', 'marionette', 'views/MapView', 'views/DesktopHeaderView', 'views/DesktopFooterView', 'models/Poi'],
function (App, Backbone, Marionette, MapView, DesktopHeaderView, DesktopFooterView, Poi) {
return Backbone.Marionette.Controller.extend({
initialize: function (options) {
App.headerRegion.show(new DesktopHeaderView());
App.mainRegion.show(new MapView());
App.footerRegion.show(new DesktopFooterView());
},
//gets mapped to in AppRouter's appRoutes
index: function () {
console.log("Ajax::list of POI");
var p = new Poi();
p.fetch({
success: function (data) {
console.log("data");
}
});
console.log(p);
}
});
});
I have no Idea where to look to debug this. The Network tab tells me that the data was fetched, but the success method is never called.
Thanks
I think your fetch call itself looks OK, but two other apparent bugs could be affecting that call:
1) Your log message in the index function says "list of Poi", but you're using a (single) Poi instance -- should that be PoiCollection instead? I'm assuming the Poi model (not shown above) is for a single item.
2) There's no url property in the PoiCollection, so if you did fetch a PoiCollection instead, that call would fail because PoiCollection doesn't know what URL to use. The most common pattern with Collection + related Model is to put an url only in the Collection, and no url in the single Model for the Collection's individual items (Poi in this case). Backbone will construct the corresponding individual-model URLs as needed based on the parent Collection's url. I think getting the url straightened out will help here.
Finally, one more thing: the fist parameter passed to the fetch call's success function is the Model or Collection instance itself, not the raw data object. That's not relevant for the current success code you have now (you're only logging a static string), but it will be relevant as soon as you try using that parameter. :-)
I have a collection (an object list) in database. I can fetch it like: collectionModel.fetch()
But then user changes something on that collection. When user clickes on save button, the whole collection list must be update in database. I thought maybe i can delete() the old one first and then create() it with new one but i could'n achive it. I can't use the update() method because in this case i should find which collection elements has changed but i want to update whole list. How can i do that? Thanks for help.
Do you have a REST api in front of that database? That's how Backbone is made to work with. When your JavaScript code runs model.save(); a PUT request is made to your api for that model.
You question is about saving the whole collection, for that if you want to remain within the default implementation of Backbone you will have to go over all the models in the collection and call save for each of them.
If you want to make one single request to your server you will have to implement a custom method inside your collection. Something like:
MyCollection = Backbone.Collection.extend({
saveAll: function() {
var data = this.toJSON();
return Backbone.$.ajax({
data: { objects: data },
url: '/url/in/your/server/to/update/db'
});
}
});
That's going to send the array of all models in your collection converted to JSON to your server.
Again, you want to have a RESTful API on the server side if you want to make your life with Backbone easy.
If you want to reset collection you have to specify "reset" attribute.
collectionList.fetch({
reset: true,
...
});
But I think it's better to just update it:
collectionList.fetch({
remove: false,
update: true,
merge: true,
...
});
This is a very old question, but I had another approach so I thought I'd post it.
Sometimes my collections have a lot of data and the server doesn't get it all. I solved this by using one of the underscore methods that backbone collections have, invoke (also relies on jquery):
MyCollection = Backbone.Collection.extend({
update: function(callback) {
// Invoke the update method on all models
$.when.apply($, this.invoke('update')).then(() => {
// After complete call the callback method (if passsed)
if(callback) {
callback();
}
});
}
});
You can use it by calling collection.update() when the collection has models in it. A similar method can be used for creating or deleting collections, and this should be modifiable to catch errors but I didn't account for that.
I have a simple List-style Backbone app that I'm making with a Rails backend.
I have a collection:
var ItemList = Backbone.Collection.extend({
model: Item,
initialize: function(id) {
this.id = id;
},
url: function(){
return '/lists/' + this.id + '/items';
},
});
All the standard CRUD operations work fine from the model. But I have an "extra" route - "clear" that will clear all the items in a list at one show. The route would be:
/lists/[:id]/clear
Because this is outside the normal CRUD operations, is there way to hook it into the normal Collection, or do i do something separate?
You can make a method on your collection called destroy and inside there you can take one of several approaches to making the AJAX request (in order of harmony with Backbone). Note you probably don't want to call your collection method clear because Backbone Models already have a clear method with different semantics.
create a throw-away Backbone.Model instance with the correct URL and ID and then call 'destroy' on it
Call Backbone.sync with method "delete" and a throw-away model object with just an 'url' property and empty 'toJSON' function with the right ID
Make a direct jQuery $.ajax call.
You could add your own method that executes /lists/:id/clear and then does a reset on the collection when it is done:
clear: function() {
var _this = this;
$.ajax({
url: '/lists/' + this.id + '/clear',
//...
success: function() {
_this.reset();
}
});
}
When you call reset without any arguments, it removes all the models from the collection.
How i can refresh model's attributes when invoke save()
I am using backbone and backbone relational. Have the following code:
saveParams: function(event){
var self = this;
this.model.save({}, {
success: function(model, resp, xhr){
model = ...
},
error: function(model, resp){
alert(JSON.stringify(resp));
}
});
$(this.el).effect("highlight", {}, 1000);
event.preventDefault();
},
When pass callback success, the parameters "model" has ald attributes (before save), resp is holding updated attributes. How i can update attributes in a model?
model.set(resp) doesn' help me
model.set(JSON.stringify) doesn't help me
UPD1: I use Backbone RelationModel cause have nested models. Nested models doesn't refresh when success callback invokes. I guess because RelationModel using Backbone.Store.
UPD2: For me works only this:
model.clear()
model.set(resp);
model.change();
I know it's ugly, but it's working )
Normally you don't have to do this!
Backbone automatically parses the response if a save() command, as you can see here:
http://documentcloud.github.com/backbone/docs/backbone.html#section-41
If your response data differs from the default assumed backbone data structure you should take a look at the Backbone.Model#parse method and maybe overwrite it (it's a very simple method).