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
Related
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.
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);
}
});
},
I would like to fetch model from specific url with parameter:
url: server/somecontroller/id/?type=gift
Simple working way is:
collection.fetch({ data: { type: 'gift'} });
But I want to set it in model:
...
if(id){
App.coupon = new AffiliatesApp.Coupon({id: id});
} else {
App.coupon = new AffiliatesApp.Coupon({id: 'somecontroller'}, {type: 'gift'});
}
App.coupon.fetch();
How can I achieve it?
The easiest way to achieve this is to override Backbone's url method on the Coupon model with one defined by you. For example you can do :
Affiliates.Coupon = Backbone.Model.extend({
urlRoot : "server/somecontroller/",
url : function(){
var url = this.urlRoot + this.id;
if(this.get("type")){
url = url + "/?type=" + this.get("type");
}
return url;
}
});
This solution is easy to implement but has a drawback: the generated URL will be used for every action that syncs to the server (fetch,save,..).
If you need to have a finer control over the generation of the URL depending on what action you are doing you will need to override Backbone's Sync method for your model.
It can be done by overriding the fetch method in model to use some custom data. Using CoffeeScript it could look like this:
class AffiliatesApp.Coupon extends Backbone.Model
fetch: ->
super(data: { type: #get('type') })
Note that this example will ignore any attributes passed to coupon.fetch(), however it can be easily adjusted for any override logic.
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.
For creating a new model I need to pass just one attribute:
model.save({
name: 'bar'
});
Then the server will add extra attributes like the id for this model.
Actually the response of the server when I create the new model is
this request has no response data available.
What is the best way to get the extra attributes created by the server?
Should I fix the sever part?
Here's my code:
var ListView = Marionette.CompositeView.extend({
// some code
events: {
'click #create-user': 'createUser'
},
createUser: function (e) {
this.collection.create(this.newAttributes(e));
},
newAttributes: function (e) {
return {
name: $(e.currentTarget).val(),
};
}
appendHtml: function (collectionView, itemView) {
collectionView.$el.find('ul.list-users').append(itemView.el);
}
});
This question is not related to Marionette, which only extends views, not models.
Backbone will automatically incorporate any new attributes sent back from the server - there is nothing special that needs to be done on the client side. But for this to work, your server needs to return a JSON object of attribute-value pairs that you want to be set.