backbonejs and oop js newbie here. i am trying to bind my collection to a view, when i go to my console, i only get this
this is a collection: [object Object]
is there something am i missing here?
var root = "http://jsonplaceholder.typicode.com";
var Post = Backbone.Model.extend({});
var Posts = Backbone.Collection.extend({
model: Post,
url: root + "/posts",
parse: function(response) {
return response;
}
});
var posts = new Posts();
posts.fetch();
// View wrapper to render view child items
var PostsView = Backbone.View.extend({
collection: new Posts(),
initialize: function() {
console.log('this is a collection: ' + this.collection);
},
render: function() {
// STEPS:
// filter through all items in a collection
// for each item, create a new PostView
// append to root element, ex. ul
}
});
var postsview = new PostsView();
When you do 'this is a collection: ' + this.collection you're forcing the collection to be a string, and javascript sees an object and turns it into your [object Object].
The correct way to show a collection as a string is to just its .toJSON() function and do console.log('this is a collection: ' + JSON.stringify(this.collection.toJSON())); but since most devtools are capable of properly showing an object natively you're better off using
console.log('this is a collection: ', this.collection);
That should give you a nice interactive representation of the collection in your console, at least on Chrome.
Related
I'm in the beginning stages of moving my application to the backbone framework.
I have some data that comes in as json from an ajax call
{f1:"f1_value", f2:"f2_value", f3:"f3_value"},
{f1:"f1_value", f2:"f2_value", f3:"f3_value"},
{f1:"f1_value", f2:"f2_value", f3:"f3_value"},
This data set always has 3 columns but may be as long as needed for each set as far as rows goes.
It is used to populate a div after processing it client side into HTML, which correlatively may extend down as far as needed. I was planning on this data chunk representing one view in the framework.
<div id = "data_hold"></div>
How do I match this up to the framework:
var ModelTest,
CollectionTest,
ViewTest;
ModelTest = Backbone.Model.extend({
});
CollectionTest = Backbone.Collection.extend({
model: ModelTest
}
ViewTest = Backbone.View.extend({
});
Backbone 101:
var ModelTest,
CollectionTest,
ViewTest;
ModelTest = Backbone.Model.extend({ });
// associate your collection with a URL. This is static here; it can be
// passed in as an option using the Collection's initialize function()
// instead.
CollectionTest = Backbone.Collection.extend({
model: ModelTest,
url: 'http://localhost/my_json_source'
});
ViewTest = Backbone.View.extend({
// Have a target to render into. This can be an existing element, as
// here, or it can be dynamically generated and attached to the DOM
// programattically.
el: $('#data_hold'),
// specify than when the collection is updated, call the local render
// method.
initialize: function(options) {
this.collection.bind('reset', _.bind(this.render, this));
},
// Empty the element, then append subsequent rows of the collection
// to it as paragraphs. The '_this = this' idiom allows us to access
// the outside context (the View's context), since the each() call
// will create a new inner context.
render: function() {
var _this = this;
this.$el.html('');
this.collection.each(function(l) {
_this.$el.append('<p>' + l.get('f2') + '</p>');
});
}
});
// initialize the collection and view, then fetch the collection, which
// will trigger the render after the collection has been updated.
$(function() {
ct = new CollectionTest();
vt = new ViewTest({collection: ct});
ct.fetch();
});
I'm creating a Backbone Model which has a child Backbone Model inside of it:
console.log("inside add item, video:", video instanceof Backbone.Model);
var playlistItem = new PlaylistItem({
video: video,
title: video.get('title')
});
afterwards, I save it:
playlistItem.save({}, {
success: function() {
console.log("Successfully saved.");
playlistItem.get('video').get('title');
}
});
In this example, I encounter an error -- video is not an instanceof Backbone.Model after calling save. Why?
I think you will need to override parse and toJSON.
toJSON: function() {
var json = Backbone.Model.prototype.toJSON.call(this);
// replace backbone model with json.
json.video = this.get('video').toJSON();
return json;
},
parse: function(data) {
// take json of video and set into model.
this.get('video').set(data.video);
delete data.video;
return data;
},
If you don't parse the json data like this, backbone is going to take the 'video' object from the json and overwrite your Backbone model.
i'm trying to populate a nested collection that's inside a model, and taht last model inside another collection.
The code is the following
App.Models.Bed = Backbone.Model.extend();
App.Collections.Beds = Backbone.Collection.extend({
model: App.Models.Bed,
initialize: function(models, options){
this.room = options.room;
}
});
App.Models.Room = Backbone.Model.extend();
App.Collections.Room = Backbone.Collection.extend({
url: 'rooms/',
initialize: function(){
this.on('reset', this.getBeds, this);
},
getBeds: function(){
this.each(function( room ){
room.beds = new App.Collections.Beds([], { room: room});
room.beds.url = 'rooms/' + room.id + '/beds';
room.beds.fetch();
console.log(room.beds.toJSON());
});
}
});
Now, if I execute:
var rooms = new App.Collections.Room();
rooms.fetch();
rooms.toJSON();
It gives me back just the rooms populated, but no beds property on each bed.
The wired thing is that it makes the request to the server at /rooms/1/beds and I get back each bed.
Is it populating the beds collection?
Am I doing something wrong?
Thanks for your time mates.
Looks like you need to pass in the Room Model to the Room Collections.
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();
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"
}
});