Module method is not being called with () - backbone.js

For some reason I can't get the addOne method to fire in this view. The render method fires just fine.The collection is being passed in from another module that instantiates it. When I add the parentheses to the method call it will fire but then Underscore throws an error.
define(['backbone',
'jquery',
'underscore',
'views/user',
'models/user',
],
function(Backbone,$,_,UserView,UserModel){
var Users= Backbone.View.extend({
tagName:'ul',
className:'well',
initialize:function() {
_.bindAll(this);
},
render:function() {
this.collection.each( this.addOne, this );
return this;
},
addOne:function() {
console.log('inside of addOne');
var userView = new UserView({model:UserModel});
this.$el.append(userView.render().el);
}
});
return Users;
});

Your UserView is a model view. This means that you need to pass to it an instance of a model and not the definition of the model's module, which is what the "UserModel" holds in your case.
Example:
var um = new UserModel({name: 'foo', id: 1});
var uv = new UserView({model: um}); //um is an instance of the model 'UserModel'
In your case, since the model objects are coming from iterating over this.collection, it would be (as correctly noted above by mu is too short):
addOne: function(m) {
console.log('inside of addOne');
var userView = new UserView({model: m});
this.$el.append(userView.render().el);
}
Also be careful so that this.collection is not empty when you call render().

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 .bind('change', _.bind(this.render, this)) not registering

I have a measure model, that is made up of two collections, a beats collections, and a measureRep[resentation] collection. Each collection is made of beat models and representation models respectively.
Whenever a measureRep[resentation] collection changes (by addition or subtraction of a representation model), I want the measureView (which has the measure model, and therefore the measureRep[resentation] collection) to re-render itself using the render function().
I am adding a new model in another View by the following function:
var representationModel = new RepresentationModel({representationType: newRepType});
StageCollection.get(cid).get('measures').models[0].get('measureRepresentations').add(representationModel);
I can see that before and after the addition that the measure and its measureRep collection are getting added correctly, however, the bind call on the measureView is not registering the change and calling the render function. I even put the bind on the model, to show that the backend is getting updated, however, it doesn't respond. This leads me to believe that the View and the models are decoupled, but that doesn't make sense, since it originally renders from the model. Here are the relevant files:
measureView.js [View]:
define([...], function(...){
return Backbone.View.extend({
initialize: function(options){
if (options) {
for (var key in options) {
this[key] = options[key];
}
this.el = '#measure-container-'+options.parent.cid;
}
window.css = this.collectionOfRepresentations; //I use these to attach it to the window to verify that the are getting updated correctly
window.csd = this.model; // Same
_.bindAll(this, 'render'); // I have optionally included this and it hasn't helped
this.collectionOfRepresentations.bind('change', _.bind(this.render, this));
this.render();
},
render: function(){
// Make a template for the measure and append the MeasureTemplate
var measureTemplateParameters = { ... };
var compiledMeasureTemplate = _.template( MeasureTemplate, measureTemplateParameters );
// If we are adding a rep, clear the current reps, then add the template
$(this.el).html('');
$(this.el).append( compiledMeasureTemplate )
// for each rep in the measuresCollection
_.each(this.collectionOfRepresentations.models, function(rep, repIndex) {
var measureRepViewParamaters = { ... };
new MeasureRepView(measureRepViewParamaters);
}, this);
return this;
},
...
});
});
measure.js [Model]:
define([ ... ], function(_, Backbone, BeatsCollection, RepresentationsCollection) {
var MeasureModel = Backbone.Model.extend({
defaults: {
beats: BeatsCollection,
measureRepresentations: RepresentationsCollection
},
initialize: function(){
var logg = function() { console.log('changed'); };
this.measureRepresentations.bind('change', logg);
this.bind('change', logg);
}
});
return MeasureModel;
});
representations.js [Collection]:
define([ ... ], function($, _, Backbone, RepresentationModel){
var RepresentationsCollection = Backbone.Collection.extend({
model: RepresentationModel,
initialize: function(){
}
});
return RepresentationsCollection;
});
I have also tried registering the bind on the measure model, and not its child collection, but neither work.
_.bindAll(this, 'render');
this.model.bind('change', _.bind(this.render, this));
see: https://stackoverflow.com/a/8175141/1449799
In order to detect additions of models to a collection, you need to listen for the add event (not the change event, which will fire when a model in the collection is changed http://documentcloud.github.io/backbone/#Events-catalog ).
so try:
this.measureRepresentations.bind('add', logg);

Backbone call a model method from the view

I have a backbone app with require where I want to add a collection inside a collection with a method inside model.
I have tried to insert the method in the collection but I can't add elements.
I'd want to make a collection of app when I click an element outside the app I want add inside my app other app in a collection.
This is my app:
Model:
define(['backbone', 'collections/element'],function(Backbone, ElementCollection){
var DesignModel = Backbone.Model.extend({
initialize:function(){
console.log('Initialized Design model');
_.defaults(this, {
elements: new ElementCollection()
});
},
addElement: function(elements, options) {
return this.elements.add(elements, options);
}
});
return DesignModel;
});
Collection:
define(['backbone', 'models/design'], function(Backbone, DesignModel){
var designCollection = Backbone.Collection.extend({
model: DesignModel,
});
return designCollection;
});
View
define(['jquery' , 'backbone', 'models/design', 'collections/design', 'views/element'],
function($, Backbone, DesignModel, DesignCollection, ElementView){
var DesignView = Backbone.View.extend({
el:$('#page'),
initialize: function(){
console.log('initialize DesignView');
this.collection = new DesignCollection();
var here = this;
$('#insert-dynamic-element').click(function(){
var element = new ElementView();
here.collection.models.addElement(element);
});
},
render: function(){
}
})
return DesignView;
});
I have tried to call the function addElement in this way:
here.collection.models.addElement(element);
and
here.collection.addElement(element);
But always with error that Object has no method addElement
How can I solve this? I want to call the method addElement from the view to add an app inside another app in a collection.
Thanks
The safest way to call the method is to add the method to the collection instead of the Model. Currently the method is available on the Model instance .
So this.collection.models.addElement will not cut it
Collection
define(['backbone', 'models/design'], function(Backbone, DesignModel){
var designCollection = Backbone.Collection.extend({
model: DesignModel,
addElement: function(elements, options) {
return this.add(elements, options);
}
});
return designCollection;
});
View
define(['jquery' , 'backbone', 'models/design', 'collections/design', 'views/element'],
function($, Backbone, DesignModel, DesignCollection, ElementView){
var DesignView = Backbone.View.extend({
el:$('#page'),
initialize: function(){
console.log('initialize DesignView');
this.collection = new DesignCollection();
var here = this;
$('#insert-dynamic-element').click(function(){
var element = new ElementView();
here.collection.addElement(element);
});
},
render: function(){
}
})
return DesignView;
});
If you do not want to move the method from the current model. Then you might have to call a specific model using the index
here.collection.at(0).addElement(element);
But there might be a case when there are no model in the collection and this might lead to a error condition..
here.collection.at(0) && here.collection.at(0).addElement(element);
Well, you need to get a specific model, not the array of them. This seems like an error since you'll be picking a specific model essentially arbitrarily (unless you application has semantics that support this), but this would work:
here.collection.at(0).addElement(element);

Why won't my model get passed?

I have this collection view
define([
'jquery',
'underscore',
'backbone',
'views/project',
'collections/project-collection',
'templates'
], function ($, _, Backbone, ProjectView, ProjectCollection, JST) {
'use strict';
var ProjectListView = Backbone.View.extend({
template: JST['app/scripts/templates/projectList.ejs'],
el: $('#content'),
render: function() {
var projectCollection = new ProjectCollection();
projectCollection.fetch();
projectCollection.each(this.addOne(),this);
return this;
},
addOne: function(project) {
console.log('addOne function');
var projectView = new ProjectView({model: project});
this.$el.html( projectView.render().el);
}
});
return ProjectListView;
});
No matter what I try the model never gets passed through to the addOne function so the in the view that is instantiated by this method the call to
this.model.toJSON()
results in the old 'cannot call method .toJSON of undefined' error. I tried to inject the collection when this collection view was instantiated and that didn't work either. Obviously here it is in the dependency array and that doesn't work either. The model is definitely there as I can log projectCollection.model to the console inside the render function. I'm stumped.
I see two problems with your render: one you know about and one you don't.
The first problem is right here:
projectCollection.each(this.addOne(), this);
The parentheses on this.addOne() call the addOne method right there rather than passing the this.addOne function to each as a callback. You want this:
projectCollection.each(this.addOne, this);
The second problem is that you have to wait for the collection's fetch to return before anything will be in the collection. You can use the fetch's callbacks:
var _this = this;
projectCollection.fetch({
success: function() {
projectCollection.each(_this.addOne, _this);
}
});
or you can use the various events that fetch will fire, see the fetch documentation for details.

How to trigger the fetch method and how to set the url

I'am redesigning my backbone application based on the answer of #20100 to this question The best way to fetch and render a collection for a given object_id.
Please read the comment on the code because I think is more clear, and my question looks better in smaller sizes.
// My View
define([
"js/collections/myCollection",
"js/models/myFeed"
], function (MyCollection, MyModel) {
var MyView = Backbone.View.extend({
tagName: 'ul',
initialize: function () {
this.collection = new MyCollection();
this.collection.on('add', this.onAddOne, this);
this.collection.on('reset', this.onAddAll, this);
// when I make myView = new MyView(_.extend( {el:this.$("#myView")} , this.options));
// myView.render is not called
// in order to trigger the render function I make the following… but probably there is a better way …
var that = this;
this.collection.fetch({
success: function () {
that.render();
}
});
}
});
return MyView;
});
// MyCollection
define([
"js/models/myModel"
], function (MyModel) {
var MyCollection = Backbone.MyCollection.extend({
model: MyModel, // add this
url: function () {
var url = "http://localhost/movies";
return url;
// if I look to the GET request the url is without idAttribute
// how can I attach the idAttribute to this url?
// should bb takes care of this?
}
});
return MyCollection;
});
//MyModel
define([
], function () {
var MyModel = Backbone.MyModel.extend({
idAttribute: 'object_id'
});
return MyModel
});
There's two paths you want to explore
Pre-populate your collection with your model data
In your example you're already doing this, but you're fetching a collection, the collection URL is http://localhost/movies, if you want an individual model take a look at the next point
Fetch each individual model only when you need it
In the assumption that you're trying to get an ID on a collection that is not pre-populated and are loading 1 model at a time, you will have to approach this a bit in a custom way by adding a method to your collection somewhat similarly to this
getOrFetch: function(id, options)
{
var model;
if (this.get(id))
{
model = this.get(id);
}
else
{
model = new this.model({
id: id
});
this.add(model);
model.fetch(options);
}
return model;
}
or add the function as Backbone.Collection.prototype.getOrFetch so you can use it on every Backbone Collection if you need it.

Resources