My model attribute in my view is being classified as a function - backbone.js

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.

Related

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.

Backbone localstorage A "url" property or function must be specified

I am creating a small application based on a backbone example with the backbone-localstorage plugin.
When saving the data for a new model I always get the error "A "url" property or function must be specified"
After reading through several simular topics I'm still not able to find the cause for this.
Model:
directory.models.EmployeeCollection = Backbone.Collection.extend({
localStorage: new Backbone.LocalStorage("EmployeeCollection"),
model: directory.models.Employee,
store: directory.utils.store,
findByName: function(key) {
this.reset(this.store.findByName(key));
}
});
The view:
directory.views.newEmployeeView = Backbone.View.extend({
tagName: "div",
initialize: function() {
this.template = _.template(directory.utils.templateLoader.get('new-employee'));
},
events: {
"click .save": "saveEmployee"
},
render: function(eventName) {
$(this.el).html(this.template(this.model.toJSON()));
return this;
},
saveEmployee: function(event){
this.model.set({
firstName:$('#newFirstName').val(),
lastName:$('#newLastName').val(),
title:$('#newTitle').val(),
city:$('#newCity').val(),
officePhone:$('#newOfficePhone').val(),
cellPhone:$('#newCellPhone').val(),
email:$('#newEmail').val()
});
this.model.save();
window.history.back();
return false;
}
});
I think you need the new model to be a member of your collection before you attempt to persist it. Try creating a new instance of your collection and passing it to the view (probably in your router) like this:
new newEmployeeView({ collection: new EmployeeCollection() });
In your view you can use Backbone's create convenience method (see docs) to add a new instance of the model to the collection and persist it:
this.collection.create({
firstName:$('#newFirstName').val(),
lastName:$('#newLastName').val(),
title:$('#newTitle').val(),
city:$('#newCity').val(),
officePhone:$('#newOfficePhone').val(),
cellPhone:$('#newCellPhone').val(),
email:$('#newEmail').val()
});

Backbone.js model property is getting not defined error

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.)

Creating a backbone view for a collection

How can I bind a backbone view to a collection rather than a model? Do I need to wrap the collection in a model?
e.g.
If I have a backbone model Client and a collection of these called Clients
Client = Backbone.Model.extend({
defaults: {
Name: ''
}
});
Clients = Backbone.Collection.extend({
model: Client,
url: 'Clients'
});
and a view
var ClientListView = Backbone.View.extend({
template: _.template($("#clients-template").html()),
el: $('#clientlist'),
initialize: function() {
_.bindAll(this, 'render');
this.collection = new Clients();
},
render: function( event ){
$(this.el).html(this.template({ this.collection.toJSON()));
return this;
}
});
then I can't access each client element in the underscore template. However if I wrap the collection like this
$(this.el).html(this.template({ clients: this.collection.toJSON() }));
then I can. Is this the correct way to go about this? I would expect this to be a common scenario but I can't find any examples on it, am I going about it the wrong way?
Yes, you need to pass the wrapped collection.
Addy Osmani is using similar approach in his Backbone Fundamentals examples - see for example this view and corresponding template:
In the view:
$el.html( compiled_template( { results: collection.models } ) );
In the template:
<% _.each( results, function( item, i ){ %>
...
<% }); %>
Another alternative is to have a view that will create separate view for each model in the collection. Here is an example from An Intro to Backbone.js: Part 3 – Binding a Collection to a View:
var DonutCollectionView = Backbone.View.extend({
initialize : function() {
this._donutViews = [];
this.collection.each(function(donut) {
that._donutViews.push(new UpdatingDonutView({
model : donut,
tagName : 'li'
}));
});
},
render : function() {
var that = this;
$(this.el).empty();
_(this._donutViews).each(function(dv) {
$(that.el).append(dv.render().el);
});
}
});
You might want to take a look at backbone collectionView.

Resources