unable to call fetch on a backbone model - backbone.js

In the function jsonRequest below, I can log this.model to the console, but I can't call this.model.fetch(), which I thought was the appropriate way to make a request to my server (at localhost:8080/jsonapi). The error it gives me is can't call fetch of undefined. Is there something I'm doing wrong in the code below?
var MyModel = Backbone.Model.extend({
url: 'jsonapi',
});
var MyView = Backbone.View.extend({
el: '#blahblah',
initialize: function(){
},
events: {
'click #start' : 'callJsonRequest',
},
callJsonRequest: function(){
setInterval(this.jsonRequest, 1000);
},
jsonRequest: function(){
console.log("jsonrequest", this.model); //this logs the model
this.model.fetch();
},
});
window.myModel = new MyModel();
window.startStop = new StartView({model: myModel});

You likely need to use bind to make sure this is the context of your View object.
As you mentioned in the comments, you can do:
setInterval(this.jsonRequest.bind(this), 1000);

Related

Backbone.js .on('add') to Collection is not causing a render

I am following CodeSchool's 'Anatomy of Backbone.js' and cannot get this to work on my machine. There are similar questions, but they have a lot of extra stuff going on and, for someone brand-new like me, it's making it hard to learn.
Here's the code as simple/universal as possible:
var WorldEvent = Backbone.Model.extend({});
var WorldEventView = Backbone.View.extend({
events: {
'click' : 'focusEvent'
},
focusEvent: function(){
alert('great.');
},
className : 'pin bounce',
render : function () {
console.log('did something');
this.$el.html("rendered");
return this;
}
});
var WorldEventCollection = Backbone.Collection.extend({
model: WorldEvent,
url: '/events'
});
var worldEventCollection = new WorldEventCollection();
var worldEventCollectionView = new WorldEventView({
collection: worldEventCollection,
initialize: function(){
this.collection.on('add', this.addOne, this);
this.collection.on('reset', this.addAll, this);
},
addOne: function(myEvent){
var worldEventView = new WorldEventView({ model: myEvent });
this.$el.append(worldEventView.render().el);
},
addAll: function(){
this.collection.forEach(this.addOne, this);
},
render: function(){
this.addAll();
}
});
The good news is that if I call
worldEventCollection.add(new WorldEvent( {<my data>} ));
... the new model is added to worldEventCollection - I've logged worldEventCollection and worldEventCollection.length to verify.
The bad news is that "did something" doesn't appear in the console and I see no evidence of a render.
Please help, I've wasted a ton of time on what is probably super simple. Thank you.
UPDATE
Okay, I found one of my issues. I needed to define a separate WorldEventCollectionView class altogether, so this was NOT correct:
var worldEventCollectionView = new WorldEventView({
collection: worldEventCollection,
...
Instead, I believe one correct approach is:
var WorldEventCollectionView = Backbone.View.extend({
initialize: function(){
this.collection.on('add', this.addOne, this.collection);
...
And then:
var worldEventCollectionView = new WorldEventCollectionView({ collection: worldEventCollection });
WorldEventView.render must end with return this; as per backbone view convention, otherwise chaining such as worldEventView.render().el will not work. Specifically, that will throw an exception since render() returns undefined and you try to access the .el property of undefined.
There's several other things that are not quite right about your snippet as well, but fix that first and see if you can take it from there. Generally in a view's render method, you want to populate HTML inside the view's this.$el and return this; at the end of render and really that's all you should be doing. Render has a very specific purpose and code that isn't following that basic idea and semantic belongs elsewhere.
Oh so this:
var worldEventCollectionView = new WorldEventView({
should be:
var WorldEventCollectionView = Backbone.View.extend({

Type Error on Backbone/Marionette Single Model Fetch

I am getting used to using Backbone and Marionette and run into a little snag that I am sure I am overlooking something. I am trying to populate my ItemView with a model from my API and I can see the request and data coming back ok but I get a Type Error:obj is undefined in what appears to be my listener:
TypeError: obj is undefined
var id = obj._listenerId || (obj._listenerId = _.uniqueId('l'));
Here is my Model/View
var MyDetailView = Marionette.ItemView.extend({
template: '#my-item-detail',
initialize: function () {
_.bindAll(this, 'render');
// bind the model change to re-render this view
this.listenTo(this.model, 'change', this.render);
},
tagName: "div"
})
var MyModel= Backbone.Model.extend({ urlRoot: '/api/model', intialize: function () { } });
And my code to execute:
var m = new MyModel({ id: 123});
m.fetch({
success: function (model, response) {
var view = new MyDetailView (model);
layout.content.show(view);
}
});
You'll need to pass the model in as an options hash and not just the first parameter to MyDetailView like so:
var view = new MyDetailView({ model: model });
Also for future reference Marionette does _.bindAll with render in the Marionette.View constructor.

Backbone.js, using urlRoot result in view

I am a Backbone.js newbie and I'm just playing around with it. I would like to know how the model is related with the View when using urlRoot. I'm using a local restful service. When calling 'api/feed/57' I get the following JSON result:
{"id":"57","name":"Speakers","name_desc":null,"url":"http:\/\/speakers.com\/feed\/","category":"1","favicon":"http:\/\/g.etfv.co\/http%3A%2F%2Fspeakers.com%2F","last_update":"2013-09-20 12:57:25","insert_date":"0000-00-00 00:00:00"}
What I want to achive is to have the values retrieved displayed by the view. When trying so, the default values are displayed and not the values retrieved from the urlRoot. I used a console.log(name) to verify if the json service is working properly. It seems so, because "speakers" is shown in the debug. Any idea what I'm doing wrong? The following code is used:
var Feed = Backbone.Model.extend({
urlRoot: 'api/feed',
defaults: {
name: 'Test',
name_desc: 'Test',
url: ''
}
});
var feedItem = new Feed({id: 57});
feedItem.fetch({
success: function (feedItem) {
var name = feedItem.get('name');
console.log(name);
}
})
var FeedView = Backbone.View.extend({
tagName: 'li',
initialize: function(){
this.render();
},
render: function(){
this.$el.html( this.model.get('name') );
}
});
var FeedView = new FeedView({ model: feedItem });
FeedView.el;
$(document.body).html(FeedView.el);
First, you are overriding your View, choose a different name to store the instance,
var feedView = new FeedView({ model: feedItem });
feedView.render();
$(document.body).html(feedView.el);
Second, the fetch is an asynchronous call so you need to wait for it to complete before rendering,
initialize: function(){
this.listenTo(this.model, 'change', this.render);
},
Now, when your model changes, your render function will be called and update the view with the correct values.

Additional Model is undefined

I am having problems including an additional model into my view which is based on a collection. I have a list of comments which is created by a parent view. Its need that I have the current user name when rendering the comments to show delete button and to highlight if its his own comment. The problem is now that I cant access in CommentListView the model session, so this.session in initialize or a call from a method like addAllCommentTo list is undefinied. What I am doing wrong here? I thought its easily possible to add another object to an view appart from the model.
CommentListView:
window.CommentListView = Backbone.View.extend({
el: $("#comments"),
initialize: function () {
this.model.bind('reset', this.addAllCommentToList, this);
this.model.bind('add', this.refresh, this);
this.model.bind('remove', this.refresh, this);
},
refresh: function(){
this.model.fetch();
},
addCommentToList : function(comment) {
console.log("comment added to dom");
//need to check why el reference is not working
$("#comments").append(new CommentView({model:comment, sessionModel: this.session}).render().el);
},
addAllCommentToList: function() {
$("#comments").empty();
this.model.each(this.addCommentToList);
}
});
Call from parent list in initialize method:
window.UserDetailView = Backbone.View.extend({
events: {
"click #newComment" : "newComment"
},
initialize: function () {
this.commentText = $("#commentText", this.el);
new CommentListView({ model: this.model.comments, session: this.model.session });
new LikeView({ model: this.model.like });
this.model.comments.fetch();
},
newComment : function() {
console.log("new comment");
this.model.comments.create(
new Comment({text: this.commentText.val()}), {wait: true}
);
this.commentText.val('');
}
});
Model:
window.UserDetail = Backbone.Model.extend({
urlRoot:'/api/details',
initialize:function () {
this.comments = new Comments();
this.comments.url = "/api/details/" + this.id + "/comments";
this.like = new Like();
this.like.url = "/api/details/" + this.id + "/likes";
this.session = new Session();
},
...
});
I see one problem, but can there be others.
You are initializing the View like this:
new CommentListView({ model: this.model.comments, session: this.model.session });
And you are expecting into your View to have a reference like this this.session.
This is not gonna happen. All the hash you send to the View constructor will be stored into this.options, from Backbone View constructor docs:
When creating a new View, the options you pass are attached to the view as this.options, for future reference.
So you can start changing this line:
$("#comments").append(new CommentView({model:comment, sessionModel: this.session}).render().el);
by this other:
$("#comments").append(new CommentView({model:comment, sessionModel: this.options.session}).render().el);
Try and tell us.
Updated
Also change this line:
this.model.each(this.addCommentToList);
by this:
this.model.each(this.addCommentToList, this);
The second argument is the context, in other words: what you want to be this in the called handler.

Backbone.js binding collection to models after a fetch using ajax

I'm trying to learn backbone.js and I'm having trouble understanding how to bind models and read them after a fetch.
This is my code:
$(function() {
var Bid = Backbone.Model.extend();
var BidsList = Backbone.Collection.extend({
model: Bid,
url: '/buyers/auction/latestBids?auctionId=26&latestBidId=0',
});
var BidsView = Backbone.View.extend({
el: $('#bids'),
initialize: function() {
log('hi');
_.bindAll(this, 'render');
this.collection = new BidsList();
this.collection.fetch();
this.render();
},
render: function() {
log(this.collection);
return this;
},
});
var bidsView = new BidsView();
});
function log(m) { console.log(m); }
This is what the webservice json looks like
{
"AuctionState":3,
"ClosedOn":null,
"Bids":[
{
"BidId":132,
"AuctionId":26
},
{
"BidId":131,
"AuctionId":2
}
]
}
How do I would I bind that response to the model?
You need to override the parse() method on your BidCollection to pull the Bids out and present them, and them only, to the collection's add() routine. You can do other things with the parse() method to manage the AuctionState field.
You also need to listen for 'change' events in your view, so the view automatically updates after the fetch. You shouldn't need to call render() in your view; you should bind the model's 'change' event to to render(), then fetch the data and let that trigger the render.
As always, Backbone's source code is highly readable. I recommend learning and understanding it.
For example:
var BidsList = Backbone.Collection.extend({
model: Bid,
url: '/buyers/auction/latestBids?auctionId=26&latestBidId=0',
parse: function(response){
return response.Bids;
}
});

Resources