I have a backbone model of a patient, which I can use to pull patients from my Mongo database. But in addition to polling them by ID, I would like to be able to pull them by name. The only way I could think of doing this is to do something like:
class Thorax.Models.Patient extends Thorax.Model
urlRoot: '/api/patients'
idAttribute: '_id'
fetch: (options = {}) ->
if #get 'first' # has first name, lookup by that instead of id
#urlRoot = '/api/patients/by_name/' + (#get 'first') + '/' + (#get 'last')
#set '_id', ''
super options
But overriding the urlRoot seems bad. Is there another way to do this?
you may just use Backbone.Model#url as a method and apply all your logic there.
So if it is first name in the model use one url and else use default url root.
Here is jsbin code for this (just convert to your CoffeeScript)
You may open network tab to see 2 XHR requests for 2 models I created they are different.
var Model = Backbone.Model.extend({
urlRoot: 'your/url/root',
url: function() {
// If model has first name override url to lookup by first and last
if (this.get("first")) {
return '/api/patients/by_name/' + encodeURIComponent(this.get('first')) + '/' + encodeURIComponent(this.get('last'));
}
// Return default url root in other cases
return Backbone.Model.prototype.url.apply(this, arguments);
}
});
(new Model({ id: 1, first: 'Eugene', last: 'Glova'})).fetch();
(new Model({ id: "patient-id"})).fetch();
Also you may apply this logic to the url in fetch options. But I don't think it is good way.
Happy coding.
Related
I have a Sitesand a Positionscollection. Each time the user selects a new site, the id is sent to the refreshPositions method which is in charge of doing the fetch call.
The route to get the positions look like this '.../sites/1/positions'
view.js
refreshPositions: function(siteId) {
this._positions.fetch({
success: this.onPositionsFetchSuccess.bind(this),
error: this.onPositionsFetchError.bind(this)
});
},
So refreshPositions is called whenever I need to update the positionson the page and the siteId parameter has the id, I just don't know to tell fetch to route to something like .../sites/n/positions where n would be the siteId .
Sorry if I missed relevant informations for my question, I'm pretty new to backbone.
I see, so you are calling fetch from your Positions Collection. The out-of-the-box functionality there is to fetch the whole collection (every Position object) if you have a RESTfull api set up. If you want more specific behaviour from your collection, you can probably write it into the Collection object definition.
var PositionCollection = Backbone.Collection.extend({
initialize: function(models, options) {
this.siteId = (options && options.siteId) || 0;
},
url: function() {
if (!this.siteId) {
return '/positions'; // or whatever
}
return '/sites/' + this.siteId + '/positions';
},
// etc...
});
Then, assuming that _positions refers to an instance of PositionCollection you can do:
refreshPositions: function(siteId) {
this._positions.siteId = siteId; // or wrap in a setter if you prefer
this._positions.fetch({
success: this.onPositionsFetchSuccess.bind(this),
error: this.onPositionsFetchError.bind(this)
});
},
I have the following collection:
AisisWriter.Collections.Posts = Backbone.Collection.extend({
model: AisisWriter.Models.Post,
// Build the url based on blog id.
url: function() {
url = '/api/v1/blogs/' + AisisWriter.blog_id + '/posts/'
return url;
}
});
this allows me to do:
var options = { reset: true };
this.writer_posts.fetch(options).then(this.postsRecieved, this.serverError);
this will return me all posts, current six.
I have tired to do:
var options = { reset: true };
this.writer_posts.fetch({id: id}).then(this.postsRecieved, this.serverError);
// id is the id passed into the route, in this case it's #=> {id: 6}
But this still returns me all six posts, I have seen this answer, but I don't think I should have to go through, or extend the model in the middle of code just to append an ID, that and I use the model for the Post, Put and Delete actions while I use the collection for fetching data.
So how do I return one post?
In general, to get some Model from Collection, you should fetch Collection and then get needed model by id, for example:
this.writer_posts.fetch();
And after Collection will be fetched you get method
this.writer_posts.get(id);
Or you can try to fetch a specific Model by passing required id in the fetch method:
this.writer_posts.fetch({
data: {
id: id
}
});
Or something like that.
I want to fire fetch method on Backbone Collection which would pass an Id parameter similar to what happens in Model.fetch(id)
E.g.
var someFoo= new Foo({id: '1234'});// Where Foo is a Backbone Model
someFoo.fetch();
My Backbone collection:-
var tasks = backbone.Collection.extend({
model: taskModel,
url: '/MyController/GetTasks',
initialize: function () {
return this;
}
});
In my View when I try to fetch data:-
var _dummyId = 10; //
// Tried approach 1 && It calls an api without any `id` parameter, so I get 500 (Internal Server Error).
this.collection.fetch(_dummyId);
// Tried approach 2 && which fires API call passing Id, but just after that
// I am getting error as below:- Uncaught TypeError: object is not a function
this.collection.fetch({
data: {
id: _dummyId
}
});
Found it very late : To cut short the above story I want something like Get /collection/id in backbone.
Thank you for your answers, finally I got the solution from Backbone.js collection options.
Apologies that I couldn't explain the question properly while for same requirement others have done brilliantly and smartly.
Solution : I can have something like :-
var Messages = Backbone.Collection.extend({
initialize: function(models, options) {
this.id = options.id;
},
url: function() {
return '/messages/' + this.id;
},
model: Message,
});
var collection = new Messages([], { id: 2 });
collection.fetch();
Thanks to nrabinowitz. Link to the Answer
As mentioned by Matt Ball, the question doesn't make sense: either you call fetch() on a Collection to retrieve all the Models from the Server, or you call fetch() on a Model with an ID to retrieve only this one.
Now, if for some reason you'd need to pass extra parameters to a Collection.fetch() (such as paging information), you could always add a 'data' key in your options object, and it may happen that one of this key be an id (+add option to add this fetched model rather than replace the collection with just one model)... but that would be a very round-about way of fetching a model. The expected way is to create a new Model with the id and fetch it:
this.collection = new taskCollection();
newTask = this.collection.add({id: 15002});
newTask.fetch();
In your code however, I don't see where the ID is coming from, so I am wondering what did you expect to be in the 'ID' parameter that you wanted the collection.fetch() to send?
I want to retrieve a collection of keywords of a file with an POST request like this:
api.host.com/file/4/keywords
But how do I have to define my url and urlRoot Keyword model and keyword collection?
I've read the docs, but I could not figured it out.
As usual, in general but especially in JS, there are many ways to do that. I can tell you a couple of ways I would do that.
1) I would define a keywords Collection as
Keywords = Backbone.Collection.extend( { ... } )
Then use it as a property of the File Model and I would set the correct value during initialize()
File = Backbone.Model.extend({
// the empty array [] is the initial set of models
this.keywords = new Keywords([], { url: '/file/' + this.id + '/keywords' });
})
This way you can call file.keywords.fetch() to get the content.
2) I would define a keywords Collection as
Keywords = Backbone.Collection.extend({
initialize: function(models, options) {
this.modelId = options.modelId
},
url: function() {
return '/file/' + this.modelId + '/keywords'
}
});
And then, when needed, I would create instance like this:
File = Backbone.Model.extend({
this.keywords = new Keywords([], { modelId: this.id });
})
To give you a complete answer, you could actually drop the initialize() function (if you don't need it) and write and url function as
url: function() {
return '/file/' + this.options.modelId + '/keywords'
}
If I have a model named "Book" and a collection named "Library" defined as below:
Book
app.Book = Backbone.Model.extend({
defaults: {
title: 'No title',
author: 'Unknown'
}
});
Library
app.Library = Backbone.Collection.extend({
model: app.Book,
url: '/api/books'
});
When I call BookInstance.save() how does it come up with the URL to use? Does it derive it from the collection?
In Backbone.model there are 2 options: url and urlRoot. What is the purpose and difference between these?
In Backbone.collection, there is a single parameter url. Is this always the GET request for the RESTFUL api?
Basically, there are 3 possibilities to construct a model's url:
If the model object exists in a collection then its url method will return an address composed of the collection.url and model.id: [collection.url]/[id].
If you don't want to use a model inside the collection, then model.urlRoot's value can be used instead of the collection.url fragment, resulting in the following pattern: [urlRoot]/[id].
Finally, if you're NOT planning to persist more that one model of a given type to the server or will be defining URLs for each model upon their creation, you can directly assign a value to model.url.
Collections send only GET requests — to get an array of models' JSON data. For saving, removing, and updating, the individual model's save() (POST/PUT/PATCH) and destroy() (DELETE) methods are used.
Here's the source code of Backbone.Model.url, which should help you:
url: function() {
var base =
_.result(this, 'urlRoot') ||
_.result(this.collection, 'url') ||
urlError();
if (this.isNew()) return base;
var id = this.get(this.idAttribute);
return base.replace(/[^\/]$/, '$&/') + encodeURIComponent(id);
}
In model
urlRoot is used for the Model.
url is used for the instance of the Model.
So if urlRoot exists
then book.fetch() will fetch the data given id, for example
var Book = Backbone.Model.extend({urlRoot: 'books' });
var book = new Book({id: 1});
book.fetch(); // will get /books/1
var Book = Backbone.Model.extend({});
var book = new Book({url: 'books/1'});
book.fetch(); // will get /books/1
var Books = Backbone.Collection.extend({model: Book});
var books = new Books({ /*....*/ });
books.fetch(); // will get /books/
You can refer the backbone Model urlRoot source code here
I hope it makes sense to you, good luck.