Has Model fetch behaviour, specifically setting the model id, changed between 1.1.0 and 1.1.2?
I've checked the changelog and can't find anything relevant.
The following no longer works:
var Wibble = Backbone.Model.extend({
urlRoot: 'rest/wibble',
idAttribute: 'wibbleId'
});
var model = new Wibble();
model.id = 1;
model.fetch()
It requests /rest/wibble rather than /rest/wibble/1 as it used to.
Examples: I've used url() rather than fetch() to demonstrate
jsbin for 1.1.0
jsbin for 1.1.2
A model builds its url by appending /[id] when the model is not new :
url: function() {
var base = _.result(this, 'urlRoot') ||
_.result(this.collection, 'url') ||
urlError();
if (this.isNew()) return base;
return base.replace(/([^\/])$/, '$1/') + encodeURIComponent(this.id);
}
but it appears that model.isNew changed between 1.1.0 and 1.1.2
Backbone 1.1.0
isNew: function() {
return this.id == null;
},
Backbone 1.1.2
isNew: function() {
return !this.has(this.idAttribute);
},
The check now only considers the property described by idAttribute and no longer the id property.
Setting your idAttribute as you did in your 1.1.2 example is probably the safest bet:
model.set('wibbleId', 123);
Related
Lets say, I have the following Backbone model :
var Meal = Backbone.Model.extend({
defaults: {
"appetizer": "caesar salad",
"entree": "ravioli",
"dessert": "cheesecake"
},
urlRoot : api/meals,
idAttribute : id,
// some other stuff
});
Assuming that I have a backend Spring MVC conroller that intercept GET requests, so when I fetch my model, using
myMeal.fetch();
my model gets loaded from the server.
for now everything is clear, my question is, what if I have another method on the backend that takes a string as parameter and return as responsebody, the right json element.
how can I make that call from my model ?
I'm looking for something like this :
var meal = new Meal({'entree': 'value'});
meal.fetch({
// if there is no id, and 'entree' is given, I want to call /
// api/findByEntree passing this.entree as parameter.
});
I want to make an Ajax call from the object itself to the backend, by specifying the url inside the Backbone model.
urlRoot can be a function so no need to override fetch. I believe you could just do something like this:
var Meal = Backbone.Model.extend({
defaults: {
"appetizer": "caesar salad",
"entree": "ravioli",
"dessert": "cheesecake"
},
urlRoot : function() {
return 'api/' + this.get('id') ? 'meals' : 'findByEntree';
},
idAttribute : id,
// some other stuff
});
You can override the default fetch, intercept the call, do some verification and then pass onto the original fetch:
var Meal = Backbone.Model.extend({
fetch: function(options) {
if(this.has('id')) {
Backbone.Model.prototype.fetch.call(this, options);
} else {
this.findByEntree(options);
}
},
fetchByEntree: function(options) {
...
}
});
however, keep in mind that you'll need some extra logic to deal with the case of trying to fetch a new Meal, which won't have neither id nor entree.
I am using backbone.js in my app. My model named MyModel is
Backbone.Model.extend({
urlRoot: 'home'
});
Need to fetch model with url "home/abc/xyz" where "abc" and "xyz" are dynamic in my view. I did the following
var model = new MyModel({id:'abc/xyz'});
model.fetch();
but its not working.
It goes to url "home/abc?xyz".
How should i solve this issue?
Here is the url function of Backbone.Model which is responsible for such kind of behavior in Backbone:
url: function() {
var base =
_.result(this, 'urlRoot') ||
_.result(this.collection, 'url') ||
urlError();
if (this.isNew()) return base;
return base.replace(/([^\/])$/, '$1/') + encodeURIComponent(this.id);
}
As you can see encodeURIComponent(this.id) will encode your id, so you can't pass and '/' -> '%2F'.
You always can rewrite this function, but I guess it's not the best idea, cause it can break another things.
I can suggest you another approach:
Just define urlRoot of your model as function and there do your job:
var yourModel = Backbone.Model.extend({
defaultUrl: 'home',
urlRoot: function () {
return defaultUrl + this.get('yourUrlAttribute');
}
});
Just pass the yourUrlAttribute as model attribute when creating it and fetch the model.
Having in mind this approach and that Backbone.Model will append encoded 'id' as the last part of URL (when you fetching model) you can get what you want.
Is it possible to override the url method of a backbone model to insert the id in the middle of the url string.
I.e. I dont want to fetch from this
documents/6
but this
documents/6/editor
and similiary update to
documents/6/editor
Currently backbone insists on always appending the id to the end of the url string.
I tried
urlRoot: function(){
return "/documents" + this.id + "/editor";
}
Whilst this works for fetching a model from the server it fails on updates. It seems to be trying the url
/documents/6/editor/6
and not
/documents/6/editor
Overriding Model.urlRoot alters the prefix of your url and is later used in Model.url
Try
var M = Backbone.Model.extend({
urlRoot: '/documents',
url: function() {
var base = Backbone.Model.prototype.url.call(this);
if (this.isNew()) return base;
return base+'/editor';
}
});
And a demo http://jsfiddle.net/nikoshr/pjr81pLd/
From my understanding the default behavior of Backbone JS Models are to return the Collection's URL, unless the model has a urlRoot specified. I can't seem to get the behavior to work.
From the documentation:
model.url() ... Generates URLs of the form: "[collection.url]/[id]" by default, but you may override by specifying an explicit urlRoot if the model's collection shouldn't be taken into account.
Here is my collection, and model respectively:
var MyCollection = Backbone.Collection.extend({
model: Model,
initialize: function(options){
this.options = options || {};
},
url: function(){
return "/theurl/" + this.options.param;
}
});
return MyCollection;
...
var MyModel = Backbone.Model.extend({
urlRoot: '/theurl',
initialize: function() {
}
});
return MyModel;
When a model is loaded without a collection, it works great and submits to /theurl, but when it's loaded into a collection, all methods submit to /theurl/param/.
If I'm understanding the documentation correctly, the urlRoot of the Model should override this behavior; and even then the models url should be /theurl/param/{MODEL-ID}.
Any ideas on what I'm missing/misunderstanding?
...
PS: The model: Model from the collection is brought in via RequireJS
It will always use the collection's url even if you have urlRoot specified.
The reason for urlRoot is so you can use it in an override, or if the model happens to not be in a collection ( for example maybe it gets removed, but still exists on the client).
So if you want to fetch or save the model and override the url generated by the collection you'll need to pass the urlRoot into these methods explicitly as an option. For example:
yourModel.save({ url: yourModel.urlRoot });
I agree the documentation is confusing and this caught me out recently too.
UrlRoot should be a function and model must have attributeId defined.
If you define your model like this all operation will be working if model is in collection or not.
Backbone add modelId at the end of URL that is returned by urlRoot function.
var MyModel = Backbone.Model.extend({
attributeId: 'myModelId'
urlRoot: function() {
return '/theurl';
},
initialize: function() {
}
defaults: {
myModelId: null
}
});
In the model, try using:
url: function() {
return 'your url goes here';
}
i try to fetch a record of a rails-api (same host) into my backbone collection. i have the following code:
// Models
App.GeeksModel = Backbone.Model.extend({
urlRoot: "/geeks",
idAttribute: "id"
});
// Collections
App.GeeksCollection = Backbone.Collection.extend({
url: "/geeks",
model: App.GeeksModel
});
in my router i have the following
// Router
App.GeekRouter = Backbone.Router.extend({
routes: {
"": "index"
},
initialize: function() {
console.log("router - init");
},
index: function() {
console.log("route - index");
var geekCollection = new App.GeeksCollection();
var mapView = new App.GeeksMapView({ el: $("#foo"), model: geekCollection });
geekCollection.fetch();
}
});
when browsing the url, the view loads correctly and at the server i see, that one entry is fetched from the database. but as soon as i check the model length in my view using
this.model.length
the collection is empty... any advice on this?
thanks
EDIT 1:
when changing the index router method to
var mapView = new App.GeeksMapView({ el: $("#map"), collection: geekCollection });
and e.g. check for the collection length in the views intialize method
...
initialize: function() {
this.render();
console.log(this.collection.length);
},
...
it retunes 0 as well... so nothing changed!
I believe you want to do collection.length or if accessing from the model - each model holds reference to collection in which it was created model.collection.length - if this is referencing to collection doing just this.length should be enough, if it's a model then this.collection.length will do it for you.
Models have no property length so should always be undefined unless you define it yourself.