Backbone.js Collections View forEach doesn't work - backbone.js

I am trying to use collections to list my data coming from my api.
But the problem is, when I use forEach, the function that I called (addOne) doesn't run.
There is also something I suspect working wrong. Should my collection save the returning JSON under the models like that?
Object -> models -> 0 -> attributes -> ...
My View:
s.Views.Fs = Backbone.View.extend({
className: "",
template: _.template("<%= name %>"),
initialize: function() {
},
render: function() {
this.collection.forEach(this.addOne, this);
},
addOne: function(f) {
alert("a");
var fV = new s.Views.PF({model: f});
this.$el.append(fV.render().el);
}
});
My Collection:
s.Collections.FL = Backbone.Collection.extend({
url: "api/fs/",
model: s.Models.F,
});
My Model:
s.Models.F = Backbone.Model.extend( {
urlRoot: 'api/fs/',
defaults: {
...
},
...
parse: function(response) {
return response;
},
});
My Route (And App):
var sApp = new (Backbone.Router.extend({
f_a: function() {
this.fL= new s.Collections.FL();
this.fLV= new s.Views.Fs({collection: this.fL});
this.fL.fetch();
this.fLV.render();
},
});

Listening for events is made by this.collection.on('add', this.addOne, this); under collection's view. Here is tested code's summary (Thanks for the tip 'mu is too short'):
VIEW
s.Views.Fs = Backbone.View.extend({
className: "",
template: _.template("<%= name %>"),
initialize: function() {
this.collection.on('add', this.addOne, this);
this.collection.on('reset', this.render, this);
},
render: function() {
this.collection.forEach(this.addOne, this);
},
addOne: function(f) {
var fV = new s.Views.PF({model: f});
fV.render();
this.$el.append(fV.el);
}
});
COLLECTION
s.Collections.FL = Backbone.Collection.extend({
url: "api/fs/",
model: s.Models.F,
});
MODEL
s.Models.F = Backbone.Model.extend( {
urlRoot: 'api/fs/',
// No need to parse here.
});
ROUTER
var sApp = new (Backbone.Router.extend({
f_a: function() {
this.fL= new s.Collections.FL();
this.fLV= new s.Views.Fs({collection: this.fL});
this.fLV.render();
$("#content").html(this.fLV.el);
this.fL.fetch();
},
});

Related

Render not being called in Backbone view

Here is my Backbone:
App.Models.Count = Backbone.Model.extend({
url: this.url,
initialize: function() {
this.fetch({
success: function(data, response) {
this.count = data.get('count');
console.log(this.count); // 9, correct answer
}
});
}
});
App.Views.Count = Backbone.View.extend({
tagName: 'span',
initialize: function(options) {
this.count = this.options.count;
console.log(options); // returns correctly
this.model.on('reset', this.render, this);
},
render: function() {
console.log('test'); // not called
this.$el.html(this.model.toJSON());
return this;
}
});
And in my route:
var mc = new (App.Models.Count.extend({'url' : 'main-contact-count'}))();
var mcv = new (App.Views.Count.extend({ model: mc }))();
console.log(mcv); // 9, correct answer
$('#contactCount').html(mcv);
As you can see, my render method is never called. Also, it seems that my view is being called before my model, based on what I see console.log'd in Firebug. Is that because of the async? Why isn't render being called?
You're using Backbone in a funky way. Here's the more standard way to do this:
App.Models.Count = Backbone.Model.extend({
urlRoot: "main-contact-count"
});
App.Views.Count = Backbone.View.extend({
tagName: 'span',
initialize: function(options) {
this.model.on('change', this.render, this);
},
render: function() {
console.log('test');
this.$el.html(this.model.toJSON());
return this;
}
});
And in the router:
var mc = new App.Models.Count();
var mcv = new App.Views.Count({model: mc});
mc.fetch();
$('#contactCount').html(mcv.el);
EDIT
It turns out you're listening to "reset" on a Backbone model. This will never happen. Try listening on "change" instead of reset:
this.model.on('change', this.render, this);

Backbone view doesn't render template passed to it

I need to be able to pass different template IDs to different routes.
(function() {
window.App = {
Models: {},
Collections: {},
Views: {},
Router: {}
};
var vent = _.extend({}, Backbone.Events);
_.templateSettings.interpolate = /\[\[(.+?)\]\]/g;
App.Router = Backbone.Router.extend({
routes: {
'' : 'index',
'send-message' : 'sendMessage',
'*other' : 'other'
},
index: function() {
t = new (App.Collections.Tables.extend({ url: 'main-contact'}))();
tables = App.Views.Tables({ collection: t, template: 'mainContactTemplate' });
$('#web-leads').html(tables.el);
},
sendMessage: function() {
// t = new (App.Collections.Tables.extend({ url: 'send-message'}))();
// tables = new App.Views.Tables.extend({ collection: t, template: template('sendMessageTemplate')});
// $('#web-leads').html(tables.el);
},
other: function() {
}
});
// Main Contact
App.Models.Table = Backbone.Model.extend({});
App.Collections.Tables = Backbone.Collection.extend({
model: App.Models.Table,
initialize: function(models, options) {
this.fetch({
success: function(data) {
//console.log(data.models);
}
});
if (options) {
this.url = this.url || options.url;
}
}
});
App.Views.Tables = Backbone.View.extend({
tagName: 'ul',
initialize: function() {
this.collection.on('reset', this.render, this);
},
render: function() {
return this.collection.each(this.addOne, this);
},
addOne: function(model) {
var t = new App.Views.Table({ model: model});
this.$el.append(t.render().el);
return this;
}
});
App.Views.Table = Backbone.View.extend({
tagName: 'li',
initialize: function(options) {
this.template = options.template;
console.log(this.options);
},
retrieveTemplate: function(model) {
return _.template($('#' + this.template).html(), model);
},
render: function() {
this.$el.html(this.retrieveTemplate(this.model.toJSON()));
return this;
}
});
new App.Router();
Backbone.history.start();
})();
But I get an error than n is undefined. I think I need to pass this.template into my retrieveTemplate function. But shouldn't it already be set? This code works, by the way, if I hard code in the name of the template ID in the retrieveTemplate function.
EDIT: the template isn't being passed from the call in the router. That's where this is breaking down.
EDIT: I took out the call to extend in the second line of the index route and now I get this._configure is not a function
WORKING VERSION:
(function() {
window.App = {
Models: {},
Collections: {},
Views: {},
Router: {}
};
var vent = _.extend({}, Backbone.Events);
_.templateSettings.interpolate = /\[\[(.+?)\]\]/g;
App.Router = Backbone.Router.extend({
routes: {
'' : 'index',
'send-message' : 'sendMessage',
'*other' : 'other'
},
index: function() {
var t = new (App.Collections.Tables.extend({ url: 'main-contact'}))();
var tables = new (App.Views.Tables.extend({ collection: t, options: {template: 'mainContactTemplate' }}))();
$('#web-leads').html(tables.render().el);
},
sendMessage: function() {
// t = new (App.Collections.Tables.extend({ url: 'send-message'}))();
// tables = new App.Views.Tables.extend({ collection: t, template: template('sendMessageTemplate')});
// $('#web-leads').html(tables.el);
},
other: function() {
}
});
// Main Contact
App.Models.Table = Backbone.Model.extend({});
App.Collections.Tables = Backbone.Collection.extend({
model: App.Models.Table,
initialize: function(models, options) {
this.fetch({
success: function(data) {
//console.log(data.models);
}
});
if (options) {
this.url = this.url || options.url;
}
}
});
App.Views.Tables = Backbone.View.extend({
tagName: 'ul',
initialize: function(options) {
this.collection.on('reset', this.render, this);
this.template = this.options.template;
},
render: function() {
this.collection.each(this.addOne, this);
return this;
},
addOne: function(model, options) {
//console.log(model);
var t = new App.Views.Table({ model: model, template: this.options.template});
this.$el.append(t.render().el);
return this;
}
});
App.Views.Table = Backbone.View.extend({
tagName: 'li',
initialize: function(options) {
//console.log(this.options);
this.template = this.options.template;
},
retrieveTemplate: function(model) {
return _.template($('#' + this.template).html(), model);
},
render: function() {
//console.log(this);
this.$el.html(this.retrieveTemplate(this.model.toJSON()));
return this;
}
});
new App.Router();
Backbone.history.start();
})();
Your router says this:
tables = App.Views.Tables({ collection: t, template: 'mainContactTemplate' });
So you're giving a template: '...' to App.Views.Tables. The initialize in App.Views.Tables looks like this:
initialize: function() {
this.collection.on('reset', this.render, this);
}
so it ignores the template option. If we look at App.Views.Table (singular!), we see this:
initialize: function(options) {
this.template = options.template;
console.log(this.options);
}
but App.Views.Table is instantiated without a template option:
var t = new App.Views.Table({ model: model});
You need to fix how you use App.Views.Table. Backbone will put a view's constructor options in this.options for you so you just need to say:
var t = new App.Views.Table({ model: model, template: this.options.template });
A couple other things to consider:
You have some accidental globals in your router's index method, you should have var t and var tables rather than just t and tables.
A view's render method conventionally returns this so that you can say $x.append(v.render().el) so you might want to adjust your render methods to match the convention.
You probably need to bind the context. Underscore can help you with that.
.bindAll or .bind should do it.
I typically just use _.bindAll during initialization as shown below.
...
initialize: function(options) {
_.bindAll(this); // apply appropriate context
this.template = options.template;
},
...
Hope this helped, best of luck.

Binding data to the DOM in Backbone

Here's my Backbone.js:
(function() {
window.App = {
Models: {},
Collections: {},
Views: {},
Router: {}
};
window.template = function(id) {
return _.template( $('#' + id).html() );
};
var vent = _.extend({}, Backbone.Events);
App.Router = Backbone.Router.extend({
routes: {
'' : 'index',
'*other' : 'other'
},
index: function() {
},
other: function() {
}
});
App.Models.Main = Backbone.Model.extend({
defaults : {
FName: ''
}
});
App.Collections.Mains = Backbone.Collection.extend({
model: App.Models.Main,
initialize: function() {
this.fetch({
success: function(data) {
console.log(data.models);
}
});
},
url: '../leads/main_contact'
});
App.Views.Mains = Backbone.View.extend({
tagName: 'ul',
initialize: function() {
this.collection.on('reset', this.render, this);
console.log(this.collection);
},
render: function() {
return this.collection.each(this.addOne, this);
},
addOne: function(main) {
var mainC = new App.Views.Main({ model: main});
this.$el.append(mainC.render().el);
return this;
}
});
App.Views.Main = Backbone.View.extend({
tagName: 'li',
template: template('mainContactTemplate'),
render: function () {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
mains = new App.Collections.Mains();
main = new App.Views.Main({ collection: mains});
new App.Router;
Backbone.history.start();
})();
What I want to do is have the data returned in the ul to be bound to a DOM element called $('#web-leads'). How do I do that, given this code? Incidentally, I've already posted about this here and tried to follow the first answer and the second answer combined. But I still don't get HTML and data bound to the DOM. The data is returning from the server correctly in my collection, so I know that's not the problem. Don't worry about the router stuff. That's for later.
Generally in Backbone you don't put data on DOM elements: you put it in views that wrap that DOM element.
That being said, if you really want to store data on the element, jQuery has a function for that:
$('#web-leads').data('someKey', 'yourData');
Then you can retrieve that data with:
$('#web-leads').data('someKey');
* EDIT *
In a comments discussion with the OP it became apparent that the real goal was simply to append a view's element to an element on the page. If the element being appended to is #web-leads, then this can be accomplished with:
$('#web-leads').append(theView.render().el);

parsing + backbonejs

how to parse the response value..
i want to get the json value from the json value....but get problem in parsing....
var ListView = Backbone.View.extend({
el: '#app-container',
initialize: function() {
_.bindAll(this,"render");
console.log('ListView init.');
this.counter = 0;
this.collection = new FieldCollection();
this.collection;
this.render(this.collection);
},
events: {
'click #add': 'addItem'
},
render: function(val) {
console.log(val);
console.log('Render called.');
},
});
my json is as follow...
[
{
"name": "qqqqqqqqqqqqqqqqqqq",
"img": "qqqqqqqqqqqqqqqqqqqq"
},
{
"name": "eeeeeeeeeeeeeeeeeee",
"img": "eeeeeeeeeeeeeeeeeee"
},
{
"name": "ggggggggggggggggggg",
"img": "gggggggggggggggggggg"
}
]
my question is that how to parse the response value...
how to access the json value in the view...
var FieldCollection = Backbone.Collection.extend({
defaults: {
model: Field
},
model: Field,
url: 'http://localhost:8080/backbonejs/myjsoncollection.json',
initialize: function() {
console.log('FieldCollection init.');
},
parse: function(response) {
console.log(response);
return response;
}
});
this is the whole code...
$(function(){
var Field = Backbone.Model.extend({
defaults: {
name: "shaleen",
img: "not found",
},
initialize: function() {
// console.log(this.attributes.name);
}
});
var FieldCollection = Backbone.Collection.extend({
defaults: {
model: Field
},
model: Field,
url: 'http://localhost:8080/backbonejs/myjsoncollection.json',
initialize: function() {
console.log('FieldCollection init.');
},
/* parse: function(response) {
console.log(response);
return response;
}*/
});
var ListView = Backbone.View.extend({
el: '#app-container',
initialize: function() {
_.bindAll(this,"render");
console.log('ListView init.');
this.counter = 0;
var jsonfield = new FieldCollection();
jsonfield.fetch();
this.render(jsonfield);
},
render:function(collection){
_.each(collection, function(model){
console.log(model.get('name'));
});
_.each(function(model){
console.log(model.get('name'));
console.log(model.get('img'));
},this);
}
});
var listView = new ListView();
});
The parse function is optional, you only need to implement it, if anything special should happen after you receive the data from the server. To isolate possible sources of errors, I would simplify your approach first by not connecting to your server. Please run the following code:
var Field = Backbone.Model.extend({});
var FieldCollection = Backbone.Collection.extend({
model: Field,
});
var ListView = Backbone.View.extend({
el: '#app-container',
initialize: function() {
this.collection = new FieldCollection();
this.collection.add([{name:'name1',img:'img1'},{name:'name2',img:'img2'}]);
this.render(this.collection);
},
render(collection){
collection.each(function(model){
console.log(model.get('name')+' '+model.get('img'));
},this);
});
If this works, apply the following 2 changes:
Change the collection to include the url:
var FieldCollection = Backbone.Collection.extend({
model: Field,
url='http://localhost:8080/backbonejs/myjsoncollection.json'
});
Change the initialize function of the View to do the fetch:
initialize: function() {
this.collection = new FieldCollection();
this.collection.fetch();
this.render(this.collection);
},
Please run the simplified version first, if that works the extended version.

Backbone.js iterating through JSON results from fetch()

I am very new to Backbone and just trying to figure out the way things work. How do I iterate through the "fetched" results?
(function($) {
console.log('Formcontainer init.');
var Field = Backbone.Model.extend({
defaults: {
id: "0",
memberlist_id: "3",
field_name: "sample-field-name",
},
initialize: function() {
console.log('Field model init.');
}
});
var FieldCollection = Backbone.Collection.extend({
defaults: {
model: Field
},
model: Field,
url: 'http://localhost:8888/getstuff/3.json',
initialize: function() {
console.log('FieldCollection init.');
}
});
var ListView = Backbone.View.extend({
el: '#app-container',
initialize: function() {
_.bindAll(this,"render");
console.log('ListView init.')
this.counter = 0;
this.collection = new FieldCollection();
this.collection.fetch();
//this.collection.bind('reset', function() { console.log('xxx')});
//this.collection.fetch();
this.render();
},
events: {
'click #add': 'addItem'
},
render: function() {
var self = this;
console.log('Render called.');
},
addItem: function() {
this.counter++;
this.$("ul").append("<li>hello world" + this.counter + "</li>");
}
});
var listView = new ListView();
})(jQuery);
I get this in my Firebug console:
Formcontainer init.
ListView init.
FieldCollection init.
GET http://localhost:8888/getstuff/3.json 200 OK
Render called.
Field model init.
Field model init.
Field model init.
Field model init.
Field model init.
It seems that the fetch() was called - since "Field model init." is in the console 5 times. But how to I output that? I would want to append the items in an unordered list.
Thanks!
Bind the reset event to your render function...
this.collection.bind('reset', this.render, this);
This gets triggered after the fetch is complete, so you can create the list...
render: function() {
var self = this;
console.log('Render called.');
this.collection.each(function(i,item) {
this.$el.append("<ul>" + item.get("field_name") + "</ul>");
});
},

Resources