How to retrieve a collection based on a model in backbone - backbone.js

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'
}

Related

backbone fetch on a nested route

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)
});
},

Backbone, get a Collection by collection ID (not RESTful)

I have a backbone collection where the collection is fetched by a url with an id parameter (not RESTful)
url: '/api/categories/?level=2&id=',
So id might be
&id=2
or
&id=45
How do I go about doing this? I've been reading different posts and some say to override Backbone Sync and others say to just do a fetch but modify the data parameters...
When you construct the Collection, pass the id as parameter (by default, Collection do not have an id property while Model do).
Then, override the url property of the Collection and pass a function:
MyCollection = Backbone.Collection.extend({
initialize : function(models, options) {
this.id = options.id;
},
model : // Your Model class
url: function() {
return '/api/categories/?id=' + this.id;
}
});
// [] is the initial, empty set of models
var coll = new MyCollection([], { id: 45 });
coll.fetch(); // the correct url will be called

Initializing nested collections with Backbone

I am trying to nest a Collection View into a Model View.
In order to do so, I used Backbone's Marionnette Composite View and followed that tutorial
At the end he initializes the nested collection view like this:
MyApp.addInitializer(function(options){
var heroes = new Heroes(options.heroes);
// each hero's villains must be a backbone collection
// we initialize them here
heroes.each(function(hero){
var villains = hero.get('villains');
var villainCollection = new Villains(villains);
hero.set('villains', villainCollection);
});
// edited for brevity
});
How would you go doing the same without using the addInitalizer from Marionette?
In my project I am fectching data from the server. And when I try doing something like:
App.candidatures = new App.Collections.Candidatures;
App.candidatures.fetch({reset: true}).done(function() {
App.candidatures.each(function(candidature) {
var contacts = candidature.get('contacts');
var contactCollection = new App.Collections.Contacts(contacts);
candidature.set('contacts', contactCollection);
});
new App.Views.App({collection: App.candidatures});
});
I get an "indefined options" coming from the collection:
App.Collections.Contacts = Backbone.Collection.extend({
model: App.Models.Contact,
initialize:function(models, options) {
this.candidature = options.candidature;
},
url:function() {
return this.candidature.url() + "/contacts";
}
)};
That's because when you're creating the contactCollection, you're not providing a candidatures collections in an options object. You do need to modify your contact collection initialization code to something like:
initialize:function(models, options) {
this.candidature = options && options.candidature;
}
That way the candidature attribute will be set to the provided value (and if not provided, it will be undefined).
Then, you still need to provide the info when you're instanciating the collection:
App.candidatures.each(function(candidature) {
var contacts = candidature.get('contacts');
var contactCollection = new App.Collections.Contacts(contacts, {
candidature: candidature
});
candidature.set('contacts', contactCollection);
});
P.S.: I hope you found my blog post useful!

How to initialise nested Backbone.js models

In my Application, I have the following JSON data format:
{
Item: {
property1: '',
...
}
}
Following the solution of this stackoverflow.com answer, I modeled my Backbond.js models the following way:
App.Models.Item = Backbone.Model.extend({
});
App.Models.ItemData = Backbone.Model.extend({
defaults: {
'Item': new App.Models.Item
}
});
I now want to bootstap the data to my App from the Backend system on the page load the following way:
var item = App.Models.ItemData({
{Item:
{property1: 'data'}
}
});
The problem I have now is that item.get('Item') returns a plain JavaScrip object and not a Backbone.Model object, because the defaults are overwritten. How can I create the Backbone.js object while ensuring that item.get('Item') is an App.Models.Item object?
I also have read that if you nest Backbone.Models, you should wirite custom getter methods, so the rest of your app dose not have to know about the internal data structure. If so, what is the right way to implement those setters and getters?
You can override the parse method on your ItemData model. No defaults required. The parse method will initialize an empty model, if one is not passed:
App.Models.ItemData = Backbone.Model.extend({
parse: function(attrs) {
attrs = attrs || {};
if(!(attrs.Item instanceof App.Models.Item))
attrs.Item = new App.Models.Item(attrs.Item);
return attrs;
}
});
And then initialize your ItemData model with the option parse:true:
var item = new App.Models.ItemData({Item:{property1: 'data'}}, {parse:true});

Backbone.js only creating first model, then returning the rest as undefined when parsing with Google Calendar

Having some issues with pulling calendar events from Google Calendar using Backbone.
When I call collection.fetch() I am only getting a length of 1 returned, when there are 13 objects in the json.
I had a look at the parse:function(response) method that I am overriding in the Collection, and it is returning all 13 objects. I had a look at the add method in backbone.js, and the issue appears to occur on line 591:
models = _.isArray(models) ? models.slice() : [models];
When I wrap the line with console.log to check the status of the models variable:
console.log(models);
models = _.isArray(models) ? models.slice() : [models];
console.log(models);
I get the following result:
[Object,Object,Object,Object,Object,Object,Object,Object,Object,Object,Object,Object,Object] backbone.js:590
[child,undefined × 12]
I'm at a loss to explain why it would be failing on add. I have checked each model by changing the parse:function(response) method in the collection to return each object, and it works fine.:
parse: function(response) {
return response.feed.entry[5];
}
I have successfully parsed Google Calendar feeds with Backbone.js before, so I fear I am missing something really simple.
If I console.log response.feed the following is returned:
This is the full class:
/**
* Backbone
* #class
*/
var Gigs = Gigs || {};
Gigs.Backbone = {}
Gigs.Backbone.Model = Backbone.Model.extend();
Gigs.Backbone.Collection = Backbone.Collection.extend({
model: Gigs.Backbone.Model,
url: 'http://www.google.com/calendar/feeds/email#email.com/public/full?alt=json-in-script&orderby=starttime&callback=?',
sync: function(method, model, options) {
options.dataType = "jsonp";
return Backbone.sync(method, model, options);
},
parse: function(response) {
return response.feed.entry;
}
});
Gigs.Backbone.Controller = Backbone.View.extend({
initialize: function() {
var self = this;
this.collection = new Gigs.Backbone.Collection();
this.collection.on('reset', this.addElements, this);
this.collection.fetch();
},
addElements: function() {
log(this.collection);
}
});
var backbone = new Gigs.Backbone.Controller();
Apparently, Google Calendar provides its entries with an id wrapped in an object 1:
"id":{
"$t":"http://www.google.com/calendar/feeds/..."
}
which Backbone seems to dislike. A lot.
One simple solution would be to overwrite the id in your parse method:
parse: function(response) {
var entries=[];
_.each(response.feed.entry, function(entry,ix) {
entry.id=entry.id.$t;
entries.push(entry);
});
return entries;
}
And a Fiddle http://jsfiddle.net/bqzkT/
1 Check https://developers.google.com/gdata/docs/json to see how Google converts its XML data to JSON.
Edit : the problem comes from the way the data is returned with a straight XML to JSON conversion (requested via the alt=json-in-script parameter) wrapping the attributes in objects. Changing this parameter to alt=jsonc yields a much simpler JSON representation. Compare a jsonc output to the json-in-script equivalent.
See https://developers.google.com/youtube/2.0/developers_guide_jsonc#Comparing_JSON_and_JSONC for more info

Resources