Possible to have sub Collections in Collection? - backbone.js

I am learning Backbone.js and try to develop revamp a project using Backbong.js. As the project was already have APIs and was using in different platforms, iphone app, andorid app and web.
My problem is, there is a API return as below
{
success: true,
response: {
posts: [{...post data...}, {...post data...}, {...post data...}],
users: [{...user data...}, {...user data...}],
categories: [{...category data...}, {...category data...}]
}
}
as the backbone documentation
a Collection can contain many models
Typically the design is
// Post Model
var PostModel = Backbone.Model.extend({
...
})
// User Model
var UserModel = Backbone.Model.extend({
...
})
// Category Model
var UserModel = Backbone.Model.extend({
...
})
var PostListCollection = Backbone.Collection.extend({
model: PostModel
})
So... How can I put these 3 types of "objects" in the a Collection? Thanks

Unsure why you'd want a collection that isn't a single collection. It sounds like (and do correct me if this is wrong), you want a neat way of fetching your data from the API and turning it in to your three collections.
You could create a model that fetches from your API, that implements a custom parse function to create three collections within it.
Granted, you'll want to define UserCollection, PostCollection and CategoryCollection beforehand.
ApplicationModel = Backbone.Model.extend({
url: '/apiUrl/',
parse: function(data)
{
var result = {};
result.users = new UserCollection(data.users);
result.posts= new PostCollection(data.posts);
result.categories = new CategoryCollection(data.categories);
return result;
}
});

you can have a model that contain the collections
var ResultModel = Backbone.Model.extend({
default: {
Users: [],
Posts: [],
Categories: []
}
});
Then implements the collection in usual way.
So, when you try to get the child collections ...
var result = new ResultModel("your result JSON object");
var users = new UserCollection(result.get("Users"));
var posts = new PostCollection(result.get("Posts"));
var categories = new CategoryCollection(result.get("Category"));
This is working well for me ... in my case

Related

Backbone localstorage - A "url" must be specified (without view)

I know there are more similar questions like this, but I really couldn't find the answer to my problem..
Here is my jsfiddle: http://jsfiddle.net/ktyghnL1/3/
Code:
var Todo = Backbone.Model.extend({
});
var Todos = Backbone.Collection.extend({
model: Todo,
localStorage: new Backbone.LocalStorage('todos-backbone'),
comparator: 'order'
});
todos = new Todos();
I am only using models and collections for my angularjs app.
When I try to create an new todo, it fails.
The models your create with var todo = new Todo(); todo.save(); don't have any information related to a local storage, you only set it on the collection.
Create a model from your todos collection, the storage will be automatically provided:
var todo = todos.create();
todo.save();
See http://jsfiddle.net/nikoshr/56awrstr/1/for a demo
Or you can create a model instance, assign it a store and add it to the collection:
var store = new Backbone.LocalStorage('todos-backbone');
var todo = new Todo();
todo.localStorage = store;
todo.save();
todos.add(todo);
http://jsfiddle.net/nikoshr/56awrstr/2/

Fetch Subset of Backbone Collection with Unique Model URL (Code & Live Example Inside)

I'm looking to fetch a subset of a backbone collection using the model's URL.
Similar to the tutorial of "Nested Collections" found at:
http://liquidmedia.org/blog/2011/01/backbone-js-part-1/
Since this requires data api, models, collections, views i've included my full code of what Im trying to create:
Code (In Node/Mongoose/Express/Rest): https://github.com/1manStartup/Backbone-URLRoot
Live Example (on my ec2): http://54.251.104.185/
I believe my problem lies somewhere with the rendering of views and fetching of model data.
What Im Trying To Do: Render all Articles Unique to Dog. Route Is:
/api/makearticle/:id
If you don't use NodeJs, my JSON Api looks like this:
{
"articlename": "feed chocolate",
"_dog": {
"name": "pitbull",
"_id": "51d0b9ad6fd59c5059000002"
},
"_id": "51d0c22a6fd59c5059000007",
"__v": 0
},
{
"articlename": "took to vet",
"_dog": {
"name": "chihuaha",
"_id": "51d0b9af6fd59c5059000003"
},
"_id": "51d0c22e6fd59c5059000008",
"__v": 0
},
Here are my models and Collections:
Rest of backbone code found at:
https://github.com/1manStartup/Backbone-URLRoot
https://github.com/1manStartup/Backbone-URLRoot/blob/master/public/javascripts/demo-routerurl.js
Dog = Backbone.Model.extend({
idAttribute: '_id',
urlRoot:'/api/makedog' ,
initialize:function () {
this.dogs = new Dogs();
this.dogs.url = this.urlRoot + "/" + this.id ;
}
});
Dogs = Backbone.Collection.extend({
model: Dog,
url: '/api/makedog'
});
Article = Backbone.Model.extend({
idAttribute: '_id',
urlRoot:'/api/makearticle' ,
initialize:function () {
this.merr = new Articles;
this.merr.url = this.urlRoot + "/" + this.id ;
}
});
Please Help ive been on this for several days. Thanks.
Your API doesn't seem RESTful, so you're probably causing yourself more trouble than necessary: Backbone expects a RESTful API by default.
In any case, you need to do something like
var Article = Backbone.Model.extend({
idAttribute: '_id'
});
var ArticleCollection = Backbone.Collection.extend({
model: Article
});
var Dog = Backbone.Model.extend({
idAttribute: '_id',
initialize:function () {
this.articles = new ArticleCollection({
url: "api/dogs/" + this.get('id');
});
}
});
Then, you can do (e.g.):
var doggy = new Dog({id: 51d0b9ad6fd59c5059000002});
doggy.articles.fetch();
var articlesForDoggy = doggy.articles;
As a side node, why are you creating a new collection of dogs each time you instanciate a new dog model instance?
Also to be noted: I would seriously rething your API design. If you're using different routes for creating models (which seems to be indicated by the 'make...' routes), you're going to have to write a whole bunch of code to get the Backbone persistence layer to work with your API.
In addition, this API doesn't really follow conventions: api/dogs/XXX shouldn't be returning a list of articles: it should be returning the dog instance with id XXX. Using a URL like api/dogs/XXX/articles would make a lot more sense for the list of articles associated with a dog.
Friendly advice: although the code above should get you on your way, you really should rethink how you're designing your API. Using a non-RESTful API with Backbone, coupled with non-standard naming conventions will basically guarantee you'll be entering a world of pain with your project.

Backbone collection export issues

I have a collection of users (model user)
model has a boolean value: isExport
i have a button that on click supposed to post to the server all the users that isExport=true
Can anyone suggest a suitable solution for this problem?
I know it's possible to wrap the collection as a model and overwrite the toJSON function
but couldn't manage it so far (can someone please give a code example?)
App.User = Backbone.Model.extend({ defaults: {isExport: false}...});
App.Users = Backbone.Collections.extend({model: App.User...});
Thanks!
Roy
Backbone collections by default don't have any write operations to the server, so you'll need to add a new method to your Collection subclass such as doExport, use .where to get the models with isExport=true and their toJSON() to get an array of objects which you can then send to the server with Backbone.sync or $.post.
Backbone comes with RESTful support.
So, if you give each collection a url pointing to the collection rest service, then with a few functions (create,save) you can handle server requests.
App.Models.User = Backbone.Model.extend();
App.Collections.Users = Backbone.Collection.extend({
url: 'users',
model: App.Models.User
});
So, this way, you can:
var users = new App.Collections.Users();
users.fetch(); // This will call yoursite.com/users
// Expecting a json request
And then you can:
users.create({ name: 'John', isExport: true });
This will send a post request to the server, in order to create a new record.
And you can check on server side if it has the flag you want.
App.Views.ExportAll = Backbone.View.extend({
el: '#exportAll',
events: {
'click': 'exportAll'
},
exportAll: function(e){
e.preventDefault();
console.log('exporting all');
console.log(this.collection.toJSON());
var exportModel = new App.Models.Export;
exportModel.set("data", this.collection.toJSON());
console.log(exportModel.toJSON());
exportModel.save();
}
});
I think this is the best solution for the problem

backbone.js: how to send constraints when doing a list GET

I'm trying to build an application using backbone.js and backbone-relational.js on the frontend, with a RESTful api run by Pyramid/Cornice/SQLAlchmeny on the backend.
At this stage, I have two models, Client (basically businesses) and Asset (the assets that the businesses own) ... and eventually there will be a number of other models, for users, sites, etc. A simplified example of the two models:
Client:
id
name
Asset:
id
make
model
purchase_cost
client_id
Now, my backbone.js code is currently working fine when fetching the data from these models from the server; I have two list views, one that shows a list of all the clients, and another that shows a list of all the assets.
My problem now is that I when I click on one of the Clients, in the list of clients, I want it to then show a list of only the Assets that belong to that particular client. No problem with the server-side part of that, it's just a filter(), my question is, how do I make backbone.js send such a constraint when it requests the list of Assets?
(While I've used RelationalModel in the code below, I'm still learning it and haven't really worked out how to make use of it yet)
window.Asset = Backbone.RelationalModel.extend({
urlRoot:"/api/assets",
defaults:{
"id":null,
"make":null,
"model":null,
"purchase_cost":null,
},
relations: [{
type: Backbone.HasOne,
type: Backbone.HasOne,
key: 'client_id',
relatedModel: Client
}]
});
window.AssetCollection = Backbone.Collection.extend({
model: Asset,
url: "/api/assets"
});
window.Client = Backbone.RelationalModel.extend({
urlRoot:"/api/clients",
defaults:{
"id":null,
"name":null
}
});
window.ClientCollection = Backbone.Collection.extend({
model: Client,
url: "/api/clients"
});
I don't think I need to show any of the Views here.
In my router, I currently have a listClients function and a listAssets (see below) function, and I think I'll need to add a listAssetsforClient(clientid) function, but I'm not sure what I'm meant to do with the clientid so that backbone.js will send it to the server as a constraint when GETting the list of Assets. I presume that whatever needs to be done will be in the AssetCollection model, but I can't see anything in the Collection API that looks appropriate. There are methods that will do filtering on an already fetched list, but it seems inefficient to be fetching an entire list of assets (and there may eventually be thousands of them) when I only need a subset of them, and can get the server to filter instead.
listAssets: function() {
$('#header').html(new AssetHeaderView().render().el);
this.assetList = new AssetCollection();
var self = this;
this.assetList.fetch({
success:function () {
self.assetListView = new AssetListView({model:self.assetList});
$('#sidebar').html(self.assetListView.render().el);
if (self.requestedId) self.assetDetails(self.requestedId);
}
});
},
Ok, I have figured it out. The fetch() method has an optional 'data' parameter which can pass the constraint. So, my new function would be:
listAssetsforClient: function(id) {
$('#header').html(new AssetHeaderView().render().el);
this.assetList = new AssetCollection();
var self = this;
this.assetList.fetch({
data: { clientid: id },
success:function () {
self.assetListView = new AssetListView({model:self.assetList});
$('#sidebar').html(self.assetListView.render().el);
if (self.requestedId) self.assetDetails(self.requestedId);
}
});
},

Setting parameters on collection methods using Backbone.Rpc

Using the Backbone.Rpc plugin [ https://github.com/asciidisco/Backbone.rpc ] I am attempting to send parameters on the read method when fetching a collection. When working with a single model instance you can add parameters to a method call by setting the value of a model attribute.
var deviceModel = Backbone.model.extend({
url: 'path/to/rpc/handler',
rpc: new Backbone.Rpc(),
methods: {
read: ['getModelData', 'id']
}
});
deviceModel.set({id: 14});
deviceModel.fetch(); // Calls 'read'
// Request created by the 'read' call
{"jsonrpc":"2.0","method":"getModelData","id":"1331724849298","params":["14"]};
There is no corresponding way that I am aware of, to do a similar thing prior to fetching a collection as there is no 'set' method available to backbone collections.
var deviceCollection = Backbone.collection.extend({
model: deviceModel,
url: 'path/to/rpc/handler',
rpc: new Backbone.Rpc(),
methods: {
read: ['getDevices', 'deviceTypeId']
}
});
// This is not allowed, possible work arounds?
deviceCollection.set('deviceTypeId', 2);
deviceCollection.fetch();
// Request created by the 'read' call
{"jsonrpc":"2.0","method":"getDevices","id":"1331724849298","params":["2"]};
Is it possible to pass parameters to collection methods using Backbone.Rpc? Or do I need to pass collection filters in the data object of the fetch method?
I updated Backbone.Rpc (v 0.1.2) & now you can use the following syntax to add "dynamic"
arguments to your calls.
var Devices = Backbone.Collection.extend({
url: 'path/to/my/rpc/handler',
namespace: 'MeNotJava',
rpc: new Backbone.Rpc(),
model: Device,
arg1: 'hello',
arg2: function () { return 'world' },
methods: {
read : ['getDevices', 'arg1', 'arg2', 'arg3']
}
});
var devices = new Devices();
devices.fetch();
This call results in the following RPC request:
{"jsonrpc":"2.0","method":"MeNotJava/getDevices","id":"1331724850010","params":["hello", "world", "arg3"]}
Ah,
okay, this is not included at the moment, but i can understand the issue here.
I should be able to add a workaround for collections which allows the RPC plugin to read
collection properties.
var deviceCollection = Backbone.collection.extend({
model: deviceModel,
url: 'path/to/rpc/handler',
rpc: new Backbone.Rpc(),
deviceTypeId: 2,
methods: {
read: ['getDevices', 'deviceTypeId']
}
});
Which will then create this response:
{"jsonrpc":"2.0","method":"getDevices","id":"1331724849298","params":["2"]};
I will take a look this evening.

Resources