Proper way of populating Backbone Collection - javascript

I have the following Backbone Model and Collection
/**
* DataPoint
*/
var DataPoint = Backbone.Model.extend({
defaults: {
ts: null,
value: null
}
});
var DataPointCollection = Backbone.Collection.extend({
model: DataPoint
});
In order to populate and do what I need to do with the data I do something similar to this:
url = '/api/v1/database/1/data';
$.getJSON(url, params, function(data) {
var dps = new DataPointCollection;
dps.add(data.datapoints);
//Now do stuff with dps
});
I'm sure there is a better way to do this with Backbone but not sure how. I feel it should be more like telling the DataPoint collection to populate itself.
How to approach this on backbone?

Have a look at the docs, fetch is what you're looking for; here's the example took from there:
var accounts = new Backbone.Collection;
accounts.url = '/accounts';
accounts.fetch();

Related

How to extract a specific model from collection,and set id model

Goodmorning,i'm a bit confused about a specific id that a model has got and the id that it's has in parse.com because my collection is linked to parse.com.
If i want to get a specific model from my collection how can i do?
An example,my collection is this:
var Proposte = Backbone.Collection.extend({
model:Proposta,
url:'https://api.parse.com/1/classes/Proposte',
});
return Proposte;
and my model is this:
var Proposta = Backbone.Model.extend({
url:"https://api.parse.com/1/classes/Proposte",
...
If i want to get a specific model from my collection how can i do?
have a try at this:
var item = Proposte.findWhere({'url':"https://api.parse.com/1/classes/Proposte"});
hare is the doc
Edit:
The code above gives you the first matched model of the collection.
If you want to get multiple models that matches the specific attributes of a model, just use where
General case:
Define idAttribute for your Model/Collection:
var Proposta = Backbone.Model.extend({
idAttribute: 'name',
});
var Proposte = Backbone.Collection.extend({
model:Proposta,
url:'https://api.parse.com/1/classes/Proposte'
});
// Done here with static data just for illustration
var collection = new Proposte([{name: 'aaa'}, {name: 'bbb'}]);
Use the defined attribute to retrieve Models from collection:
console.log(collection.get('aaa'));
JSFIddle
URL, of course, can also be that attribute (just in case):
var Proposta = Backbone.Model.extend({
idAttribute: 'url'
});
var Proposte = Backbone.Collection.extend({
model:Proposta,
url:'https://api.parse.com/1/classes/Proposte',
});
var collection = new Proposte([{url: 'https://api.parse.com/1/classes/Proposte/1'}, {url: 'https://api.parse.com/1/classes/Proposte/2'}]);
console.log(collection.get('https://api.parse.com/1/classes/Proposte/2'));

Having different models for request and response - backbone.js

//A backbone model
var RequestModel = Backbone.Model.extend({});
//A backbone model
var ResponseModel = Backbone.Model.extend({});
RequestModel.save({
success: function (ResponseModel ) {
alert(ResponseModel .toJSON());
}
})
Can i have a separate Model for Request and Response, as both Request and Response does not match. Its a total RPC call and not a CRUD operation.
I've thought about this same problem before, and I feel there isn't a great way to achieve this in Backbone. The best I've come up with is to implement a fromResponse and toRequest method on the model, and override model.parse and model.sync to map the model object to them. Something like:
var Model = Backbone.Model.extend({
fromResponse: function(responseAttrs) {
var modelAttrs = {}; //map response attributes to modelAttrs
return modelAttrs;
},
toRequest: function() {
//map model attributes to response attributes here
var modelAttrs = this.toJSON();
var responseAttrs = {}; //map models attributes to requestAttrs
return responseAttrs;
},
parse: function(response) {
return this.fromResponse(response);
},
sync: function(method, model, options) {
options = options || {};
options.data = this.toRequest();
Backbone.sync(method, model, options);
}
});
If the parse and sync are overridden in some kind of a base class, then you only need to implement the fromResponse and toRequest mappers for each model.
Another option would be to override Backbone.sync altogether, and map each Model type to some kind of ModelRequestMapper and ModelResponseMapper object to (de-)serialize each model. I feel that would be more complicated, but might scale better, if you have lots of models.
/Code sample not tested

Backbone collection.add is not working

I have a pretty basic setup like this:
var MusicModel = Backbone.Model.extend({});
var PlaylistCollection = Backbone.Collection.extend({
model: MusicModel,
events: {'add':'add'},
add: function(mdl){
//This is working perfectly fine even output of model
console.log(mdl);
}
});
var playlistCollection = new PlaylistCollection();
playlistCollection.add(new Music(data));
The model is not actually added to the collection. If I try to use Chrome console and enter playlistCollection.length it will output 0 and playlistCollection.models will output [].
Any idea what I am doing wrong?
Collections in Backbone already have an add method. By writing your own, you mask the base method and prevent the the normal behavior: inserting a model into the collection. Rename your method to something else or call the base method to solve your problem:
var PlaylistCollection = Backbone.Collection.extend({
model: MusicModel,
add: function(model, opts){
Backbone.Collection.prototype.add.call(this, model, opts);
console.log(model);
}
});
http://jsfiddle.net/nikoshr/WPrTu/

How do I render multiple records out to a html table with Backbone.js ?

var ContractModel = Backbone.Model.extend({
url: "${g.createLink(controller:'waiverContract', action:'index')}"
})
var contract = new ContractModel({});
contract.fetch();
var contracts = new Backbone.Collection.extend({
model: contract
});
var ContractView = Backbone.View.extend({
initialize: function(){
this.render();
},
render: function() {
var root = this.$el;
_.each(this.model, function(item) {
var row = '<tr><td>' + item + '</td></tr>';
root.find('tbody').append(row);
});
return this;
}
});
var cView = new ContractView({ model: contract, el: $('#contracts') });
I have Chrome's developer tools open. If I do a console.log(this.model) inside of the render function, I can see a mess of an object, of which the two records are stored in .attributes. But instead of two rows being added to the table, I get 7. 6 of which are objects. (Though I see 9 subobjects in Chrome's console).
Not much of this makes sense to me. Can anyone help me not only get this working, but also understand it? I know that render() fires off as soon as I have instantiated cView, and I know that it's doing the ajax as soon as I do .fetch() into the model. But that's the limit of what I can understand in this.
You should fetch and iterate on the collection, not the model. A model is one "thing" and a collection has many "things". Assuming you are fetching a JSON formatted array into your model, it will end up with properties like "1", "2", and so on, and each of these will just be a normal Javascript object, not a ContractModel instance.
Here is how you might restructure your code:
var ContractModel = Backbone.Model.extend();
var ContractCollection = Backbone.Collection.extend({
//ContractModel class, not an instance
model: ContractModel,
//Set the url property on the collection, not the model
url: "${g.createLink(controller:'waiverContract', action:'index')}"
})
var ContractView = Backbone.View.extend({
initialize: function(){
//Bind the collection reset event, gets fired when fetch complets
this.collection.on('reset', this.render, this);
},
render: function() {
//This finds the tbody element scoped to your view.
//This assumes you have already added a tbody to the view somehow.
//You might do this with something like
//this.$el.html('<table><tbody></tbody></table>');
var $tbody = this.$('tbody');
this.collection.each(function(contract) {
//Add any other contract properties here,
//as needed, by calling the get() model method
var row = '<tr><td>' + contract.get('someContractProperty') + '</td></tr>';
//Append the row to the tbody
$tbody.append(row);
});
return this;
}
});
//Instantiate collection here, and pass it to the view
var contracts = new ContractCollection();
var cView = new ContractView({
collection: contracts,
el: $('#contracts')
});
//Makes the AJAX call.
//Triggers reset on success, and causes the view to render.
//Assumes a JSON response format like:
// [ { ... }, { ... }, ... ]
contracts.fetch();

How do I fetch a single model in Backbone?

I have a Clock model in Backbone:
var Clock = Backbone.Model.extend({});
I'm trying to get an instance of that that has the latest information from /clocks/123. Some things I've tried:
a "class"-level method
Clock.fetch(123)
// TypeError: Object function (){ ... } has no method 'fetch'
creating an instance and then calling fetch on it:
c = new Clock({id: 123})
c.fetch()
// Error: A 'url' property or function must be specified
a collection
I tried creating an AllClocks collection resource (even though I have no use for such a thing on the page):
var AllClocks = Backbone.Collection.extend({
model: Clock,
url: '/clocks/'
});
var allClocks = new AllClocks();
allClocks.fetch(123);
// returns everything from /clocks/
How do I just get one API-backed Clock?
Try specifying urlRoot in the model:
From the docs:
var Book = Backbone.Model.extend({urlRoot : '/books'});
var solaris = new Book({id: "1083-lem-solaris"});
solaris.fetch();
Your second approach is the approach I have used. Try adding the following to your Clock model:
url : function() {
var base = 'clocks';
if (this.isNew()) return base;
return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + this.id;
},
This approach assumes that you have implemented controllers with the hashbang in your URL like so, http://www.mydomain.com/#clocks/123 , but it should work even if you haven't yet.
I personally recommend, following the Model#url method documentation
model = new Model(id: 1)
view = new View(model: model)
collection = new Collection([model])
model.fetch()
in your collection remember to add the collection url:
url: "/models"
and in your View's initialize function do:
this.model.bind("change", this.render)
this way backbone will do an ajax request using this url:
"/models/1"
your model will be updated and the view rendered, without modifying Collection#url or Model#urlRoot
note:
sorry this example came out in coffee script, but you can easily translate it to js adding var statements
var Person = Backbone.Model.extend({urlRoot : '/person/details'});
var myName = new Person({id: "12345"});
myName.fetch();
As a result you make a Ajax request on the
URL http://[domainName]/person/details/id
and you have the JSON response back.
Enjoiiii !!!
...and do this if you don't want the trailing slash on the model urlRoot:
url : function() {
return this.urlRoot + this.id;
},
You probably should be accessing the object trough a collection and keeping it in the collection all the time. This is how to do it:
var AllClocks = Backbone.Collection.extend({
model: Clock,
url: '/clocks/'
});
var allClocks = new AllClocks();
my_clock = allClocks.add({id: 123});
my_clock.fetch()
I want to use RESTful url,but I couldn't understand why 'postId' can't be added to base url.
var PostModel = Backbone.Model.extend({
urlRoot: 'getBlogPost',
defaults: {
postTitle: "defaultTitle",
postTime: "1970-01-01",
postContent: "defaultContent",
postAuthor: "anonymous"
}
});
var post = new PostModel({
postId: 1
});
alert(post.url());
Then I know only after I set 'idAttribute' as 'postId' in Model can I get the right url.
like this:
var PostModel = Backbone.Model.extend({
idAttribute: 'postId',
urlRoot: 'getBlogPost',
defaults: {
postTitle: "defaultTitle",
postTime: "1970-01-01",
postContent: "defaultContent",
postAuthor: "anonymous"
}
});

Categories

Resources