Backbone.js model property is getting not defined error - backbone.js

I'm very new to Backbone.js and am trying to get this simple example working. Basically, in jsFiddle when I run the code it tells me that the property "firstname" is not defined.
Here's a link to the fiddle:
http://jsfiddle.net/cpeele00/YjUBG/16/
var User = Backbone.Model.extend({});
var UserList = Backbone.Collection.extend({
model: User
});
var UserView = Backbone.View.extend({
el: $('#user-list ul'),
template: _.template($('#user-list-template').html()),
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
var user1 = new User();
user1.set({
firstname: 'Momo',
lastname: 'Peele'
});
var user2 = new User();
user2.set({
firstname: 'Bobo',
lastname: 'Peele'
});
var users = new UserList([user1, user2]);
var userView = new UserView({model: users});
userView.render();
​
Any help figuring this out would be greatly appreciated.
V/R
Chris

Since the model is actually a collection, you need to iterate over it, and apply the template to each model in the collection. One way is to use the Underscore extension Collection.each:
render: function() {
// clear the view
this.$el.empty();
// save a reference to the view object
var self = this;
// iterate over the collection
this.model.each(function(singleModel) {
// render the model
self.$el.append(self.template(singleModel.toJSON()));
});
return this;
}
Here's the updated Fiddle.
(You could also put the iteration into the template itself if you like, but I think it's generally preferable to keep code in the view, rather than the template.)

Related

My model attribute in my view is being classified as a function

So I am trying to link my view to my model and I am following the instructions perfectly, however when it comes to the model part I am just stumped.
Whenever I try to define the model via instantiation, the model is being classified as a function when I console.log() it out.
But let me show you.
var ListModel = Backbone.Model.extend({
defaults: {
name: "Miles",
last: "Coleman"
}
});
var ListView = Backbone.View.extend({
initialize: function(opts){
this.template = opts.template;
this.render();
},
render: function() {
var data = this.model.toJSON();
console.log(this.model);
// outputs: function (){a.apply(this,arguments)}
}
});
var view = new ListView({
model: ListModel,
el: 'div',
template: _.template('#todo-template')
});
Is there some silly detail that I'm missing here? Thanks!
You're passing the class itself, ListModel, to the view, but a view expects an instance of the class, new ListModel() for example. Try
var view = new ListView({
model: new ListModel(),
el: 'div',
template: _.template('#todo-template')
});
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript for more information on classes and instances in JS.

Backbone model empty after collection fetch

var app = {};
var FoodModel = Backbone.Model.extend({
url: "http://localhost/food"
});
var FoodCollection = Backbone.Collection.extend({
model: FoodModel,
url: "http://localhost/food"
});
var FoodView = Backbone.View.extend({
render: function(){
console.log(this.model.toJSON());
}
});
app.FoodModel = new FoodModel();
app.FoodCollection = new FoodCollection;
app.FoodCollection.fetch();
var myView = new FoodView({model: app.FoodModel})
In this piece of code, the console.log always returns null for data in this.model
If console.log the collection it is full of data, how can i get this.model inside the view to reflect the data in the collection?
I'm not sure where your app is triggering FoodView.render(). It won't happen automatically when you instantiate FoodView and it won't happen when the json response triggers a callback to app.FoodCollection.fetch() unless you are manually calling that in a success callback. If you are doing this, your context may have been lost.
Also, there are a few typos in your code (app.FoodCollection = new FoodCollection();). If not, then can you provide the exact code? Please include whatever code is calling render()
Also, I'm not sure your view is being associated with your model. Try:
var FoodView = Backbone.View.extend({
model: FoodModel,
render: function(){
console.log(this.model.toJSON());
}
});
Otherwise, Backbone has no way of knowing what model you are referring to.

How To Change Backbone Dynamic URL with Backbone Events/Vent?

Edited This Below
In this image below I have two main regions.
One for the user list on the left: allusersRegion
And another for the the right side where a layout is displayed, which contains unique attributes to the user that was clicked in the allusersRegion and a list of articles by the user: middleCoreRegion
**If you noticed the middleCoreRegion is showing all articles by all users..This is wrong and I am trying to show all articles of the individual user (in this case. "kev")
I tried to see if my problem was with my JSON api (served via node/rest/mongoose) or with my underscore templates, but if it displays both list then I suppose I need to filter from inside backbone.
At first I tried using a Marionette.vent to simply change the url, but somhow I can't get the _id name into the url: function(), it says undefined...
var someuser = this.model.get("_id");
myApp.vent.trigger("showarticles", someuser);
I add a listener in the backbone collection on the same page:
myApp.vent.on("showarticles", someuser);
**The Edit (A Different Way of Doing this) Here is my code
var usertab = Poplive.module('usertab', {
startWithParent: true,
});
usertab.addInitializer(function() {
User = Backbone.Model.extend({});
UniqueArticle = Backbone.Model.extend({});
//Collections
Users = Backbone.Collection.extend({
model: User,
url: '/api/user2'
});
UniqueArticles = Backbone.Collection.extend({
model: UniqueArticle,
url: '/api/survey'
});
//Layout
var VisitingLayoutView = Backbone.Marionette.Layout.extend({
template: "#visiting-layout",
regions: {
firstRegion: "#listone",
secondRegion: "#listtwo",
thirdRegion: "#listthree",
playRegion: "#playhere",
articlesRegion: "#articleshere"
}
});
AllUserView = Backbone.Marionette.ItemView.extend({
template: "#tab-alluser-template",
tagName: 'li',
events: {
"click #openprofile" : "OpenProfile"
},
OpenProfile: function(){
console.log("Profile is open for " + this.model.get("username"));
var modelo = this.model.get("_id");
var vlv = new VisitingLayoutView({model: this.model});
Poplive.middleCoreRegion.show(vlv);
var ua = new UniqueArticles();
var uacoll = new UniqueArticlesView({collection: ua});
vlv.articlesRegion.show(uacoll);
}
})
//ItemViews
UniqueArticleView = Backbone.Marionette.ItemView.extend({
template: "#unique-article-template"
});
//CollectionViews
AllUsersView = Backbone.Marionette.CompositeView.extend({
template: "#tab-allusers-template",
itemView: AllUserView
});
UniqueArticlesView = Backbone.Marionette.CollectionView.extend({
template: "#unique-articles-template",
itemView: UniqueArticleView
});
//Render Views
var alluserview = new AllUserView();
var allusersview = new AllUsersView();
//Fetch Collections
var theusers = new Users();
theusers.fetch();
var userscoll = new AllUsersView({collection: theusers});
Poplive.allusersRegion.show(userscoll);
});
Assuming UniqueArticle to be the Backbone Model, for the Model with a specific id to be fetched you would need to define the urlRoot property which will append the id of the model to the request.
So the id attribute will be appended to the end of the request the model from the server when you do a fetch on it
var UniqueArticle = Backbone.Model.extend({
idAttribute : 'someuser',
urlRoot : function(someuser){
return '/api/visitingarticles/'
}
// this would send a request for
// /api/visitingarticles/someId
});
var UniqueArticles = Backbone.Collection.extend({
model: Article,
url : function(someuser){
return '/api/visitingarticles/'
}
// /api/visitingarticles -- All Articles will be fetched
});
I think what you want, is to define url as a function, and have a user attribute on your collection:
var UniqueArticles = Backbone.Collection.extend({
model: Article,
initialize: function(){
var self = this;
myApp.vent.on("showarticles", function(someuser){
self.user = someuser;
self.fetch();
}
},
url : function(){
var fragment = '/api/visitingarticles/';
if(this.user && this.user.id){
return fragment + this.user.id;
}
return fragment;
}
});
(Disclaimer: untested code, but it works in my head :D)
Then each time you trigger the event, the userattribute is updated, the collection is reset with the updated url.
As a side note, you might want to look into using a filtered collection. I've implemented that idea in my book, based on Derick Bailey's code here: http://jsfiddle.net/derickbailey/7tvzF/
Here is my version: https://github.com/davidsulc/marionette-gentle-introduction/blob/master/assets/js/entities/common.js
And an example of its use (lines 38-41): https://github.com/davidsulc/marionette-gentle-introduction/blob/master/assets/js/apps/contacts/list/list_controller.js#L38

Backbone.JS collection reset event does not appear to fire

I have created a simple Todo application on JS Fiddle to learn Backbone.JS. I have a TodosModuleView that wraps a form and TodosView which renders the Collection of Todos
window.TodosModuleView = Backbone.View.extend({
tagName: 'section',
id: 'todoModule',
events: {
'keypress #frmTodo input[type=text]': 'addTodo'
},
initialize: function() {
_.bindAll(this, 'render',
'addTodo');
this.collection.bind('reset', this.render);
this.template = _.template($('#todosModuleTmpl').html()); },
render: function() {
console.log('rendering...');
var todosView = new TodosView({ collection: this.collection });
this.$el.html(this.template({}));
this.$el.append(todosView.render().el);
return this;
},
addTodo: function(e) {
if (e.keyCode !== 13)
return;
var todo = new Todo({ title: this.$('input[name=todo]').val() });
this.collection.add(todo);
console.log('added!');
return false;
}
});
When I add a todo, I can see it added to the collection, but it does not appear to trigger render(). Also since I am using a Local Storage store, I'd expect that my newly added Todos should be persisted and rendered on next refresh, but that does not appear to happen. Looking at the Chrome's developer toolbar, I don't see anything in Local Storage
UPDATE
1st Problem solved with #mashingan's answer: use add instead of reset event. Now whats wrong with my Local Storage?
window.Todos = Backbone.Collection.extend({
model: Todo,
localStorage: new Backbone.LocalStorage('todos')
});
Could it be that variables are passed by value instead of reference as I'd expect? I have a TodosModuleView that uses TodosView to render the todo list, maybe I am doing it the wrong way?
Your LocalStorage isn't working because you're not saving anything. This:
var todo = new Todo({ title: this.$('input[name=todo]').val() });
this.collection.add(todo);
simply creates a new model and adds it to the collection, there is no hidden todo.save() call in there so the new Todo doesn't get saved. You'd have to save it yourself:
var todo = new Todo({ ... });
todo.save();
this.collection.add(todo);
You could also save everything in the collection with:
this.collection.invoke('save');
That will call save on each model in the collection. This might make sense for LocalStorage but not so much sense if you're persisting to a remote server.
If you do this:
var M = Backbone.Model.extend({});
var C = Backbone.Collection.extend({
model: M,
localStorage: new Backbone.LocalStorage('pancakes')
});
var c = new C;
c.add([
{ title: 'Fargo' },
{ title: 'Time Bandits' }
]);
Then you won't get anything in your pancakes database, but if you add c.invoke('save') at the end:
var M = Backbone.Model.extend({});
#...
c.add([ ... ]);
c.invoke('save');
You will get a couple of good movies saved.
Demo: http://jsfiddle.net/ambiguous/ZV86g/
Check backbone catalog of events: reset (collection) — when the collection's entire contents have been replaced. There is add event which should work in your case.

how to access a models data from a view in backbone.js

I have a model named person:
var person = Backbone.Model.extend({
initialize: function(){
console.log('cool');
},
defaults:{
names:['a','k','d','s','h','t']
}
})
Now I have a view:
var person_view = Backbone.View.extend({
model : person,
output: function(){
console.log(this.model.get('names'))
}
});
Created an object of the view:
var obj = new person_view()
Try to access names:
obj.output()
But I got this error:
TypeError: Object function (){ parent.apply(this, arguments); } has no method 'get'
Can you show me how to do things properly?I've only just started getting to know backbone.js so please bear with me.
You have to initialize your Model before you could access it :
var person_view = Backbone.View.extend({
initialize: function() {
this.model = new person();
},
output: function(){
console.log(this.model.get('names'))
}
});
Instead of passing the model when you extend the view, you'll want to pass it when you construct a new view:
var person_view = Backbone.View.extend({
output: function(){
console.log(this.model.get('names'))
}
});
var obj = new person_view({
model : new person()
});
Your "person_view" can not access any model (which is expected by that view) ,as no model is created yet, when you are declaring "person_view" and calling its function.
First make a model then pass it to view when declaring that "person_view".
var model_person_for_view= new person();
var obj = new person_view(model:model_person_for_view);
obj.output();

Resources