Tree view is not rendering in Backbone Marionette - backbone.js

I want to show tree in Backbone Marionette.
For this i create a model
applicationManager.Models.menuItem = Backbone.Model.extend({
initialize: function(){
var
children = this.get('children');
if (children){
this.children = new applicationManager.Collections.menu(children);
this.unset('children');
}
}
});
A collection
applicationManager.Collections.menu = Backbone.Collection.extend({
model: applicationManager.Models.menuItem,
url: '/en/panel/menu'
});
Recursive view
applicationManager.Views.menuItem = Backbone.Marionette.CompositeView.extend({
tagName: 'li',
template: '#menu-template',
initialize: function(){
this.collection = this.model.children;
},
appendHtml: function(collectionView, childView) {
collectionView.$('ul:first').append(childView.el);
},
onRender: function() {
if (_.isUndefined(this.collection)){
this.$('ul:first').remove();
}
}
});
And root view
applicationManager.Views.menu = Backbone.Marionette.CollectionView.extend({
tagName: 'ul',
childView: applicationManager.Views.menuItem
});
My application code is
var
menu = new applicationManager.Collections.menu();
menu.on('reset', function(data){
var
view = new applicationManager.Views.menu({
collection: data
});
applicationManager.getRegion('menuRegion').show(view);
});
menu.fetch({
reset: true
});
But when code is executing, it's no tree structure. Elements follow consecutive from each other.
Can anyone help me?
Best regards, Evgeniy.

The problem was in code for old versions of Marionette.
For now I use this
applicationManager.Views.menuItem = Backbone.Marionette.CompositeView.extend({
tagName: 'ul',
template: '#menu-template',
initialize: function(){
this.collection = this.model.children;
},
attachHtml: function(collectionView, childView){
collectionView.$('li:first').append(childView.el);
}
});
applicationManager.Views.menu = Backbone.Marionette.CollectionView.extend({
childView: applicationManager.Views.menuItem,
});

Related

Cannot read property 'toJSON' of undefined + backbone

I am really new to backbone, and in a world of confusion behind it. Right now I am trying to render a view of a single person, preferably without iterating through the entire collection, using the collection.get() method.
Here is the user collection:
App.Collections.UserCollection = Backbone.Collection.extend({
url: "/users",
model: App.Models.User,
initialize: function(){
console.log('users collection');
},
});
The user model:
App.Models.User = Backbone.Model.extend({
rootURL: '/users',
initialize: function(){
console.log('User model being generated');
}
});
the Users View (all users):
App.Views.Users = Backbone.View.extend({
el: "body",
model: 'user',
initialize: function(){
console.log('User view rendering');
this.collection.fetch();
this.listenTo(this.collection, 'reset', this.addAll);
},
clearDiv: function(){
var container = $('#render-area');
$('body').removeClass('homepage');
$('#main-nav').fadeIn(100);
container.empty();
this.addUser();
},
addUser: function(){
var current_user_model = this.collection.get(currentUser);
var user = new App.Views.ViewUser({ model: current_user_model });
$('#render-area').append(user.el);
},
events: {
'click #view-profile' : 'clearDiv'
}
});
and the viewUser view (single user):
App.Views.ViewUser = Backbone.View.extend({
initialize: function(){
console.log("single user view");
this.template = HandlebarsTemplates['user_profile'];
this.render();
},
render: function(){
$('#render-area').html(this.template(this.model.toJSON()));
},
});
I thought that I was grabbing the single user model in the Users view, and passing it to the ViewUser view with these lines
var current_user_model = this.collection.get(currentUser);
var user = new App.Views.ViewUser({ model: current_user_model });
But it seems that I am not. Any help and/or explanation is appreciated, thanks!
did you check the this.collection.get is return an object?
Maybe it can not fetch the currentUser?
before render the view you should
a) check the model is a valid model before calling the view and render a "not found" view or
b) in the view check if the model is valid and render a "not found" view
c) or you can handle it in handlebars template
Example A (untested):
var current_user_model = this.collection.get(currentUser);
if (current_user_model) {
var user = new App.Views.ViewUser({ model: current_user_model });
} else {
new AppViews.ViewUserNotFound();
}
Example B :
App.Views.ViewUser = Backbone.View.extend({
initialize: function(){
console.log("single user view");
this.template = HandlebarsTemplates['user_profile'];
this.templateNotFound = HandlebarsTemplates['user_profile_not_found'];
this.render();
},
render: function(){
if(this.model) {
$('#render-area').html(this.template(this.model.toJSON()));
} else {
$('#render-area').html(this.templateNotFound());
}
},
});
var current_user_model = this.collection.get(currentUser);
var user = new App.Views.ViewUser({ model: current_user_model });
Example C:
App.Views.ViewUser = Backbone.View.extend({
initialize: function(){
console.log("single user view");
this.template = HandlebarsTemplates['user_profile'];
this.templateNotFound = HandlebarsTemplates['user_profile_not_found'];
this.render();
},
render: function(){
if(!this.model) {
this.model = new this.model(); // create model with defaults
}
$('#render-area').html(this.template(this.model.toJSON()));
},
});
var current_user_model = this.collection.get(currentUser);
var user = new App.Views.ViewUser({ model: current_user_model });
all examples untested..
hope this helps a little bit,
best Carsten
I would do that:
App.Views.Users = Backbone.View.extend({
el: "body",
model: 'user',
initialize: function(){
console.log('User view rendering');
this.listenTo(this.collection, 'reset', this.addAll);
},
clearDiv: function(){
var self = this;
this.collection.fetch({
success: function() {
var container = $('#render-area');
$('body').removeClass('homepage');
$('#main-nav').fadeIn(100);
container.empty();
self.addUser();
}
});
},
addUser: function(){
var current_user_model = this.collection.get(currentUser);
var user = new App.Views.ViewUser({ model: current_user_model });
$('#render-area').append(user.el);
},
events: {
'click #view-profile' : 'clearDiv'
}
});

Backbone.js CollectionView not rendering html

I'm kind of new to Backbone. I'm stuck and I can't find the bug that prevents this code from rendering the html to the #times_day element. The console.log says that the collection's el is an empty div I don't know why:
TheModel = Backbone.Model.extend({
urlRoot: '/times/api/day_list'
});
DayCollection = Backbone.Collection.extend({
model: TheModel,
url: '/times/api/day_list'
})
DayView = Backbone.View.extend({
tagName: 'li',
render: function(){
return '<h3>' + this.model.get('comments') + this.model.get('minutes') + '</h3>'
}
})
CollectionView = Backbone.View.extend({
initialize: function(){
this.collection.on('reset', this.addAll, this);
},
addAll: function(){
this.collection.forEach(this.addOne, this);
},
addOne: function(dayItem){
var dayView = new DayView({model:dayItem});
dayView.render()
this.$el.append(dayView.el);
},
render: function(){
this.addAll();
}
});
var dCollection = new DayCollection({})
var cView = new CollectionView({collection:dCollection});
dCollection.fetch({
success: function(){
cView.render();
console.log(cView.el);
$("#times_day").html(cView.el);
}
})
Maybe it is a simple bug I can't see.. or maybe it is a more important concept I need to learn. Thanks a lot for your help.
DayView = Backbone.View.extend({
tagName: 'li',
render: function(){
this.$el.html('<h3>' + this.model.get('comments') + this.model.get('minutes') + '</h3>');
}
})
instead returning string in render you should set the html to view's el as you are appending el element in collectionView

Super simple-minimal Backbone script requested

Looking for the absolute minimum script to get Backbone working. Tried piecing various tutorials and sample together, but having problems getting views to work. Nothing fancy, I'll take raw json in the browser right now. Just a basic skeleton to help connect the dots and build on. I've tried various variations on the following:
(function ($) {
var model = Backbone.Model.extend({
idAttribute: 'custId'
});
var collection = Backbone.Collection.extend({
initialize: function(){
},
model: model,
url: '/cust'
});
var view = Backbone.View.extend({
initialize: function(){
_.bindAll(this, 'render'); // fixes loss of context for 'this' within methods
this.collection.bind("reset", this.render);
this.render();
},
el: $('#content'),
template: Handlebars.compile($("#contentTemplate").html()),
render: function(){
$(this.el).html( this.template(this.model.toJSON()));
},
tagName: "li"
});
var router = Backbone.Router.extend({
initialize: function(){
var newCollection = new collection;
newCollection.fetch();
},
route: {
"": "home"
},
home: function(){
this.view = new view({collection: newCollection});
$('#content').html(this.view.el);
}
});
var app = new router();
}(jQuery))
Thanx.
You are misusing the el attribute. $('#content').html(this.view.el) will result in copying the $('#content') element inside itself (because view.el is equal to $('#content')).
You should try removing the el attribute from the view object and let it generate itself. Then $('#content').html(this.view.el); should work.
One other possible problem is that you are rendering the entire collection inside a li element - was this what you are going for? The best way to go about this would be to have each model in the collection represent a li tag and the collection a ul tag.
Other issues:
the view element is receiving a collection but you are trying to render a model
in the router, newCollection is not accessible in the home method
You are not calling Backbone.history.start()
Here is how i would rewrite the code:
var model = Backbone.Model.extend({
idAttribute: 'custId'
});
var model_view = Backbone.View.extend({
template: Handlebars.compile($("#modelTemplate").html()),
tagName: 'li',
initialize: function() {
_.bindAll(this, 'render');
this.render();
this.on('change',this.render);
},
render: function() {
$(this.el).html( this.template(this.model.toJSON()) );
return this;
}
});
var collection = Backbone.Collection.extend({
initialize: function(){
},
model: model,
url: '/cust'
});
var collection_view = Backbone.View.extend({
tagName: "ul",
initialize: function(){
_.bindAll(this, 'render','renderModels');
this.render();
this.renderModels();
this.collection.bind("reset", this.render);
this.collection.bind("reset", this.renderModels);
},
render: function(){
// just create the 'ul' tag; we will populate it with model view elements; a collection template is no longer needed
return this;
},
renderModels: function() {
this.collection.each(function(obj){
var view = new model_view({
model: obj
});
$(this.el).append(view.el);
},this);
}
});
var router = Backbone.Router.extend({
initialize: function(){
this.newCollection = new collection();
this.newCollection.fetch();
},
route: {
"": "home"
},
home: function(){
this.view = new collection_view({collection: this.newCollection});
$('#content').html(this.view.el); // #content should not be a 'ul' tag, the 'ul' is generated by the collection_view
}
});
var app = new router();
Backbone.history.start();
Make sure you update your templates accordingly.
Please excuse possible errors, i had no means to test the code but i believe it points out the logic you should use.
Cheers!

Backbone.js not rendering

My collection is not rendering for some reason. Cannot find out why.
TreeItem = Backbone.Model.extend({
});
TreeList = Backbone.Collection.extend({
model: TreeItem,
url: "/get_tree_list"
});
window.tree_list = new TreeList();
// VIEW
window.TreeItemView = Backbone.View.extend({
tagName: 'li',
initialize: function(){
_.bindAll(this, 'render');
},
render: function(){
$(this.el).html('<span>'+this.model.get('title')+'</span>');
return this;
}
});
window.TreeListView = Backbone.View.extend({
el: "#tree-structure",
events: {
},
initialize: function() {
_.bindAll(this, 'appendItem', 'render');
tree_list.bind('add', this.appendItem);
tree_list.fetch();
this.render();
},
render: function() {
tree_list.each(this.appendItem);
return this;
},
appendItem: function(item){
var tree_item_view = new TreeItemView({
model: item
});
$(this.el).append(tree_item_view.render().el);
}
});
var tree_list_view = new TreeListView;
Backbone.js provides a lot to be interpreted that's where people new go wrong. Your mistake is fundamental in nature. You tie the View directly to the model
see initialize function where a instance of collection is rendered!!
Always and anywhere you create model, collection pass then as parameters to the constructor of views. Check my fiddle
Never call render inside model, view or collection. They must be inside application file
JsFiddle
http://jsfiddle.net/35QGM/

Backbone "reset" collection trigger not firing

Im using backbone.js to get a collection from the REST server. Fetch triggers fine and fills the collection with the data. However, the "reset" trigger is never fired and so addAll() is never called. Calling addAll() manually works fine - but why isn't "reset" firing as it should when fetch() is called on the collection?
Here is the code:
Model
define(['backbone-tastypie'], function(Backbone) {
var Machine = Backbone.Model.extend({
url: function(){
return this.get('resource_uri') || this.collection.url;
}
});
return Machine;
});
Collection
define(['backbone-tastypie','models/machine'], function(Backbone, Machine) {
var Machines = Backbone.Collection.extend({
model: Machine,
url: '/api/rest/machine/',
parse: function(data){
return data.objects;
}
});
return Machines;
});
Model View
define(['underscore','backbone-tastypie'], function(_, Backbone) {
var MachineTableEntryView = Backbone.View.extend({
tagName: 'tr',
template: _.template($('#machine-row-template').html()),
render: function(){
$(this.el).html(this.template(this.model.toJSON()));
console.log('lol');
return this;
}
});
return MachineTableEntryView;
});
Main View
define(['underscore','backbone-tastypie','collections/machines','views/machine_table_entry'], function(_, Backbone, Machines, MachineTableEntryView) {
var MachineTableView = Backbone.View.extend({
el: $('#app'),
initialize: function(){
_.bindAll(this, 'addOne', 'addAll');
Machines.bind('reset', this.addAll);
this.machines = new Machines();
this.machines.fetch();
},
addAll: function(){
this.machines.each(this.addOne);
},
addOne: function(machine){
var view = new MachineTableEntryView({model:machine});
this.$('#machines').append(view.render().el);
},
});
return MachineTableView;
});
You need to bind the event listener to the instance of the collection you create, not the Collection constructor:
define(['underscore','backbone-tastypie','collections/machines','views/machine_table_entry'], function(_, Backbone, Machines, MachineTableEntryView) {
var MachineTableView = Backbone.View.extend({
el: $('#app'),
initialize: function(){
_.bindAll(this, 'addOne', 'addAll');
this.machines = new Machines();
this.machines.bind('reset', this.addAll);
this.machines.fetch();
},
addAll: function(){
this.machines.each(this.addOne);
},
addOne: function(machine){
var view = new MachineTableEntryView({model:machine});
this.$('#machines').append(view.render().el);
},
});
return MachineTableView;

Resources