Error : CollectionView require ItemView - backbone.js

My example :
var stooges = [{ name: 'moe', age: 44, userid: 1},
{ name: 'larry', age: 44, userid: 2},
{ name: 'curly', age: 44, userid: 3}];
var StoogeModel = Backbone.Model.extend({});
var StoogeCollection = Backbone.Collection.extend({
model: StoogeModel
});
var StoogeItemView = Backbone.Marionette.ItemView.extend({
tagName: "tr",
template: '#stooge-template'
});
var StoogesCollectionView = Backbone.Marionette.CollectionView.extend({
tagName: "table",
childView: StoogeItemView
});
var myStooges = new StoogeCollection(stooges);
var myStoogesView = new StoogesCollectionView({ collection: myStooges });
myStoogesView.render();
document.body.appendChild(myStoogesView.el);
This example I read in topic backbone.js collection view example using marionette template but I have error:
marionette_backbone.js:1299 Uncaught NoItemViewError: An itemView must be specified
Help me please.

You're using Marionette 1.x as a dependency in your project, but you're attempting to use 2.x interfaces. In 1.x CollectionViews used an "itemView", while 2.x changed the naming to "childView"
Changing your StoogesCollectionView definition to use the itemView naming should fix your issue:
var StoogesCollectionView = Backbone.Marionette.CollectionView.extend({
tagName: "table",
itemView: StoogeItemView
});
Alternatively, you can upgrade Marionette to a newer version.

Related

Can't conditionally render view marionette

I can't figure out how to use a function to decide which child view to render in marionette. It seems like it should be pretty simple based on the documentation here: https://marionettejs.com/docs/master/marionette.collectionview.html#collectionviews-childview
I found that page from the composite view docs which inferred that using a function to define a childView should be the same for collection and composite views https://marionettejs.com/docs/master/marionette.compositeview.html#compositeviews-childview)
However, with the following code I am getting the error message "Uncaught TypeError: view.on is not a function." My code is below:
var Backbone = require('backbone');
var Marionette = require('backbone.marionette');
var ToDoModel = require('./models/todo');
var ToDo = Marionette.LayoutView.extend({
tagName: 'li',
template: require('./templates/todoitem.hbs')
});
var TodoList = Marionette.CompositeView.extend({
el: '#app-hook',
template: require('./templates/todolist.html'),
childView: function(item) {
return ToDo;
},
childViewContainer: 'ul',
ui: {
assignee: '#id_assignee',
form: 'form',
text: '#id_text'
},
triggers: {
'submit #ui.form': 'add:todo:item'
},
collectionEvents: {
add: 'itemAdded'
},
modelEvents: {
invalid: 'itemInvalid'
},
onAddTodoItem: function() {
this.model.set({
assignee: this.ui.assignee.val(),
text: this.ui.text.val()
});
if (this.model.isValid()) {
var items = this.model.pick('assignee', 'text');
this.collection.add(items);
}
},
itemAdded: function() {
this.model.set({
assignee: '',
text: ''
});
this.ui.assignee.val('');
this.ui.text.val('');
},
itemInvalid: function() {
console.log('this item is invalid!')
}
});
var todo = new TodoList({
collection: new Backbone.Collection([
{assignee: 'Scott', text: 'Write a book about Marionette'},
{assignee: 'Andrew', text: 'Do some coding'}
]),
model: new ToDoModel()
});
todo.render();
Why isn't the ToDo view being rendered?
It appears as if you are using an older version of Marionette (LayoutView for example was removed in version 3) and referencing the documentation for the newest version (currently 3.5.1).
In older versions of Marionette, childView as a function is not supported, instead you should use getChildView
So, the relevant portion of your code should look like:
var TodoList = Marionette.CompositeView.extend({
...
getChildView: function(item) {
return ToDo;
},
...
});

Why is my view not rendering?

I'm learning BackboneJS using a book called beginning backbone,
as far as I understood I can render my own el elements.
however when I call the render function it doesn't render anything on the page,
when I use console.log(view.el); it outputs what should be rendered so I guess its an issue with the render function.
var Book = Backbone.Model.extend({
defaults:
{
title: "default title",
author: "default author",
pages: 0
}
});
var Library = Backbone.Collection.extend({
model: Book
});
var View = Backbone.View.extend({
initialize: function()
{
this.render();
},
render: function()
{
this.$el.html('Hello Library');
return this;
}
});
var book1 = new Book({title: "title1",author:"author1",pages: 11});
var book2 = new Book({title: "title2",author:"author2",pages: 2});
var library = new Library([book1,book2]);
var view = new View({
model: book1,
tagName: 'ul',
className: 'page',
attributes: {'data-date': new Date()}
});
Try this: http://jsfiddle.net/tonicboy/nWvRy/
The problem you had is that you specified tagName and className, which will render a detached node. You must then manually attach that node onto some place on the screen for it to appear. The other option (which I have done) is to specify an el attribute for an element already on the screen, then the view will be rendered (attached) to that pre-existing node. You can use el or tagName, className and attributes but not both.
HTML:
<div id="view-wrapper"></div>
JS:
var Book = Backbone.Model.extend({
defaults:
{
title: "default title",
author: "default author",
pages: 0
}
});
var Library = Backbone.Collection.extend({
model: Book
});
var View = Backbone.View.extend({
initialize: function()
{
this.render();
},
render: function()
{
this.$el.html('Hello Library');
return this;
}
});
var book1 = new Book({title: "title1",author:"author1",pages: 11});
var book2 = new Book({title: "title2",author:"author2",pages: 2});
var library = new Library([book1,book2]);
var view = new View({
model: book1,
el: '#view-wrapper',
attributes: {'data-date': new Date()}
});
UPDATE:
Here's another version which uses tagName, className and attributes. Notice how the view render() method has to attach it to an existing element.
http://jsfiddle.net/tonicboy/nWvRy/1/

Two Collections inside a Composite View

So we are working on a project using marionette and we have made a good progress so far, but we struggling with a part of the marionette nested view model,
so lets assume that we have an apartment (represented as a composite view), and the apartment contains a collection of rooms and a collection of chairs, what we want to do is have the rooms and chairs a direct descending of the aprtment composite view, how can we do this, knowing that the composite view can only have one child collection, should we be using regions?
Have you tried using a Layout instead? it supports regions and an itemview (if needed). The way I am using this is to define several regions in the layout; show a collection view or item view in each region and any other apartment stuff in the layout template. so, for your example, your apartment layout would contain all of the apartment attributes, and a chairs region would contain a chairs collection view, and a rooms region could contain a rooms collection view.
You can do this with nested composite views. For the use case you described you could nest a compositeView for your Apartments and Rooms.
Fiddle:
http://jsfiddle.net/yCD2m/23/
Markup
<div id="apartments"></div>
<script type="text/html" id="appartment">
<div>
<h2>Apartment: <%=apartment%></h2>
<ul></ul>
</div>
</script>
<script type="text/html" id="room">
<h3><%=name%></h3>
<ul></ul>
</script>
<script type="text/html" id="chair">
<b><%=chairType%></b>
</script>
JS
var apartments = [
{apartment: '1a', rooms: [
{name: 'master bed', chairs: []},
{name: 'kitchen', chairs: [
{chairType: 'stool'}, {chairType: 'stool'}]},
{name: 'living room', chairs: [
{chairType: 'sofa'}, {chairType: 'love seat'}]}]
},
{apartment: '2a', rooms: [
{name: 'master bed', chairs: []},
{name: 'kitchen', chairs: [
{chairType: 'shaker'}, {chairType: 'shaker'}]},
{name: 'living room', chairs: [
{chairType: 'sectional'}]}]
}];
var chairModel = Backbone.Model.extend({});
var roomModel = Backbone.Model.extend({
initialize: function(attributes, options) {
this.chairs = new Array();
_.each(attributes.chairs, function(chair){
this.chairs.push(new chairModel(chair));
}, this);
}
});
var ApartmentModel = Backbone.Model.extend({
initialize: function(attributes, options) {
this.rooms = new Array();
_.each(attributes.rooms, function(room){
this.rooms.push(new roomModel(room));
}, this);
}
});
var ApartmentCollection = Backbone.Collection.extend({
model: ApartmentModel
});
var ChairView = Backbone.Marionette.ItemView.extend({
template:'#chair'
});
var RoomView = Backbone.Marionette.CompositeView.extend({
template: '#room',
itemViewContainer: 'ul',
itemView: ChairView,
initialize: function(){
var chairs = this.model.get('chairs');
this.collection = new Backbone.Collection(chairs);
}
});
var ApartmentView = Backbone.Marionette.CompositeView.extend({
template: '#appartment',
itemViewContainer: 'ul',
itemView: RoomView, // Composite View
initialize: function(){
var rooms = this.model.get('rooms');
this.collection = new Backbone.Collection(rooms);
}
});
var ApartmentCollectionView = Backbone.Marionette.CollectionView.extend({
itemView: ApartmentView // Composite View
});
apartmentCollection = new ApartmentCollection(apartments);
apartmentCollectionView = new ApartmentCollectionView({
collection: apartmentCollection
});
App.apartments.show(apartmentCollectionView);

Backbone/Underscore uniqueId() Odd Numbers

I'm relatively new to Backbone and Underscore and have one of those questions that's not really an issue - just bugging me out of curiosity.
I built a very simple app that allows you to add and remove models within a collection and renders them in the browser. It also has the ability to console.log the collection (so I can see my collection).
Here's the weird thing: the ID's being generated are 1,3,5... and so on. Is there a reason specific to my code, or something to do with BB/US?
Here's a working Fiddle: http://jsfiddle.net/ptagp/
And the code:
App = (function(){
var AppModel = Backbone.Model.extend({
defaults: {
id: null,
item: null
}
});
var AppCollection = Backbone.Collection.extend({
model: AppModel
});
var AppView = Backbone.View.extend({
el: $('#app'),
newfield: $('#new-item'),
initialize: function(){
this.el = $(this.el);
},
events: {
'click #add-new': 'addItem',
'click .remove-item': 'removeItem',
'click #print-collection': 'printCollection'
},
template: $('#item-template').html(),
render: function(model){
var templ = _.template(this.template);
this.el.append(templ({
id: model.get('id'),
item: model.get('item')
}));
},
addItem: function(){
var NewModel = new AppModel({
id: _.uniqueId(),
item: this.newfield.val()
});
this.collection.add(NewModel);
this.render(NewModel);
},
removeItem: function(e){
var id = this.$(e.currentTarget).parent('div').data('id');
var model = this.collection.get(id);
this.collection.remove(model);
$(e.target).parent('div').remove();
},
printCollection: function(){
this.collection.each(function(model){
console.log(model.get('id')+': '+model.get('item'));
});
}
});
return {
start: function(){
new AppView({
collection: new AppCollection()
});
}
};
});
$(function(){ new App().start(); });
if you look in the backbone.js source code you'll notice that _.uniqueId is used to set a model's cid:
https://github.com/documentcloud/backbone/blob/master/backbone.js#L194
that means that every time you create a model instance, _.uniqueId() is invoked.
that's what causing it to increment twice.

The 'add' event does not seem to fire when fetching

I am getting started with Backbone.js but can't seem to get the simplest proof-of-concept working. I have the following code:
$(function() {
var Contact = Backbone.Model.extend({
url: 'contacts.txt'
});
var ContactList = Backbone.Collection.extend({
model: Contact,
url: 'contacts.txt'
});
var Contacts = new ContactList();
var ContactView = Backbone.View.extend({
tagName: 'li',
template: _.template($('#contact-template').html()),
initialize: function() {
_.bindAll(this);
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
var AppView = Backbone.View.extend({
el: '#contacts',
initialize: function() {
Contacts.bind('add', this.addOne, this);
Contacts.fetch();
},
addOne: function(Contact) {
var view = new ContactView({model: Contact});
this.$el.append(view.render().el);
}
});
var app = new AppView();
});
The file contacts.txt contains a simple JSON structure, which is loading fine according to Chrome:
[{"from_acct_id": 5, "ct": 0, "from_display_nm": "Name 1", "to_acct_id": 1},
{"from_acct_id": 3, "ct": 1, "from_display_nm": "Name 2", "to_acct_id": 1},
{"from_acct_id": 2, "ct": 0, "from_display_nm": "Name 3", "to_acct_id": 1},
{"from_acct_id": 4, "ct": 1, "from_display_nm": "Name 4", "to_acct_id": 1}]
For whatever reason, the addOne() function bound to the add event in AppView is never invoked. What could be going wrong?
A Backbone collection's fetch call will fire a reset event. Bind to reset instead.
http://documentcloud.github.com/backbone/#Collection-fetch

Resources