I'm kinda lost so any help would be much appreciated. (I’m using Backbone.js and CoffeeScript.)
I have a group of models. They are all put in MasterCollection.
MasterCollection extends Backbone.Collection
model: Model
MasterCollection.add({#attributes of a new model})
I need to separate these models at times and process their attributes in batches. These batches also need to have a corresponding DOM view that can show all of the models’ data.
Model extends Backbone.Model
initialize: () ->
#add the model to it's batch, batches are collections stored in an array
batches = ParentModel.get('baches')
#find the batch this model belongs in
for batch in batches
if batch = #the right one
batch.add(#toJSON)
Batch extends Backbone.Collection
changeAttributes: () ->
for model in #models
#change things about the model
When this model is changed by the batch, will it update the model in the MasterCollection?
When I’m done with a batch collection, how do I get rid of it without deleting its models?
Should I store these batch collections in something better than an array? Should they be models?
Since I need the DOM to bind to the creation of new batches, having them as models in a collection would be great.
Is this a good way to do this type of thing overall?
Thanks!
When this model is changed by the batch it will update the model in the MasterCollection?
Since you're doing
batch.add(#toJSON)
you're really just adding a clone of the model to the batch collection. So, when you change that collection's model's attributes, the originals won't be affected.
Of course, these are shallow copies, so if you do something like
(batch.at(0).get 'attr').x = y
you will be modifying the attr attribute of the original. (You also won't trigger any change events.) This is a no-no with Backbone in general. Instead, do something like
attrCopy = _.extend {}, batch.at(0).get 'attr'
attrCopy.x = y
batch.at(0).set attr: attrCopy
Related
I'm just learning js/backbonejs and I have a simple question, Please feel free to direct me to a duplicate.
I have a Collection, I've populated it and I can access it in the console by doing the standard:
collection1.at(1).get('name');
I can also loop through the values by doing:
for(vars i = 0; i < collection1.size(); i++)
{
console.log(collection1.at(i).get('name'));
}
I have four buttons and have views on them and functions that correctly output something to the console when i click on them. When i click on the Show all button i want to display every model in the collection along with the data it has (id,name,fame);
How would i go about doing this? I know i have to have a
<ul id = "gottaChangeThis"></ul>
How would I be able to add something like this to it:
<li><%=id%><%=name%><%=fame%></li>
Any help or redirection would be helpful, Thanks
The basic architecture could include a Backbone.View that accepts your Collection. On render, iterate through the Models in the collection, and for each one render a different Backbone.View (to render the <li>) and append it to the parent <ul> element.
As an alternative, consider using Marionette. It's a Backbone framework/extension that gives you additional objects as a means to eliminate a lot of boilerplate. In your case, you'd want a Marionette.CollectionView with a childView specified. This childView could be a Marionette.ItemView, such that when you render the CollectionView, it automagically instantiates and renders a childView for each Model in your Collection.
I have complex nested structure of backbone relational models.
Every time I destroy a model, it is expected that the all models from relation are destroyed.
How do I do it?
Apparantely Backbone-Relational does not take care of it.
I would prefer to overload the Backbone model destroy method of your custom models. So you can destroy your nested models. And after that you can destroy the model with Backbone.Model.prototype.destroy.call(this);
Example:
var MyModel = Backbone.Model.extend({
destroy: function(){
var xhr = this.myNestedModel.destroy();
xhr.always(_.bind(function(){
Backbone.Model.prototype.destroy.call(this);
},this));
//or if you don't want to wait for the response without always
//Backbone.Model.prototype.destroy.call(this);
}
});
A different approach to overloading destroy would be to use events to propagate changes. The exact setup and complexity would depend on the relationships you have defined and also on how you create and destroy your models.
I started to learn Marionette.View concept.for this I created model with 1 attribute and assign default value.
I have dropdown list in my html file. Whenever it changes it triggers a function name as myfunction.Inside myFunction I changed model attribute value.
Whenever attribute value changes automatically it triggers another function.that event I written inside Marionette.CompositeView.but it's not working.
Earlier I did same thing using myModel.on there it's working fine.But it's not working modelEvents in Marionette.CompositeView.
let's check the following code.
var mymodel=Backbone.Model.extend({
defaults:{
"name":"oldValue"
}
});
var mymodelObj=new mymodel();
//marionette.View code
var backView=Backbone.Marionette.CompositeView.extend({
events:{
"change #pop1":"myFunction"
},
myFunction:function(){
mymodelObj.set({name:"updatedValue"})
},
modelEvents:{
"change:name":"myFunction1"
},
myFunction1:function(){
alert("Hi");
}
});
//creating instance
var backViewObj=new backView({el:$("#sidebar")});
How can I fix this.
Right Now I am trying to understanding where Marionette.js useful in my Backbone Applications.
If I am not mistaken, model is not attached to the view you have created. For modelEvents to be triggered, there should be a model attribute in CompositeView. For this you should specify the model during initialization of CompositeView;
var backViewObj=new backView({el:$("#sidebar"), model : mymodelObj});
To do this though you need to pass the model in when creating the backView like so:
var backViewObj = new backView({ el:$("#sidebar"), model: myModel });
Marionette accomplishes this by overriding delegateEvents which Backbone.View calls in it's constructor and delegating your modelEvents object to the model:
Marionette.bindEntityEvents(this, this.model, Marionette.getOption(this, "modelEvents"));
Based on your comments above I think that you're unclear about the different types of views that Backbone.Marionette provides as you are asking how to pass more model instances. I also was unclear when I began using Marionette.
Marionette.ItemView represents a single model.
Marionette.CollectionView represents a collection and renders a Marionette.ItemView for every model in the collection. It does not have the ability to render a template.
Marionette.CompositeView represents both a model and a collection (leaf and branch in a tree structure). This is for recursive data structures although you can use a CompositeView to render a template (like a table header, etc.) and use itemViewContainer to place the itemViews in a specific element.
If you want your CompositeView to be able to render multiple models you'll need to pass a collection of models into the CompositeView and not one single model:
Do I need to bind to every collection that is instantiated of the same type or do I bind to a common change event that passes in a reference to itself?
example: What would an interface with 5 different todo lists look like? Would they each need a unique id? I'm guessing they would be placed into another collection of todo lists? Any code examples would be great. Thanks.
Edit: Sorry if I'm still not clear.
TodoList is a collection of Todo models.
My app needs to display any given number of TodoList's on it. What is the best way to organize different instances of these?
generally speaking, you don't need to do anything special to handle multiple instances of a collection.
MyCollection = Backbone.Collection.extend({ ... });
MyView = Backbone.View.extend({ ... });
c1 = new MyCollection();
c2 = new MyCollection();
v1 = new MyView({collection: c1});
v2 = new MyView({collection: c2});
each view will only reference it's collection, and there is no need to worry about data being mis-matched between the two collections. when you add a model to one, it won't show up in the other. when you manipulate the view for one, it won't manipulate the view for the other (assuming you've written your view correctly).
you'll need a to coordinate how each view for each collection is displayed on the screen. this would work like any collection / item view setup, where you have a collection view that instantiates each child view, renders the child view, and then displays the child view's el within it's own el.
I understand how to get a collection together, or an individual model. And I can usually get a model's data to display. But I'm not clear at all how to take a collection and get the list of models within that collection to display.
Am I supposed to iterate over the collection and render each model individually?
Is that supposed to be part of the collection's render function?
Or does the collection just have it's own view and somehow I populate the entire collection data into a view?
Just speaking generally, what is the normal method to display a list of models?
From my experience, it's the best to keep in your collection view references to each model's view.
This snippet from the project I'm currently working on should help you get the idea better:
var MyElementsViewClass = Backbone.View.extend({
tagName: 'table',
events: {
// only whole collection events (like table sorting)
// each child view has it's own events
},
initialize: function() {
this._MyElementViews = {}; // view chache for further reuse
_(this).bindAll('add');
this.collection.bind('add', this.add);
},
render: function() {
// some collection rendering related stuff
// like appending <table> or <ul> elements
return this;
},
add: function(m) {
var MyElementView = new MyElementViewClass({
model: m
});
// cache the view
this._MyElementViews[m.get('id')] = MyElementView;
// single model rendering
// like appending <tr> or <li> elements
MyElementView.render();
}
});
Taking this approach allows you to update views more efficiently (re-rendering one row in the table instead of the whole table).
I think there are two ways to do it.
Use a view per item, and manipulate the DOM yourself. This is what the Todos example does. It's a nice way to do things because the event handling for a single model item is clearer. You also can do one template per item. On the downside, you don't have a single template for the collection view as a whole.
Use a view for the whole collection. The main advantage here is that you can do more manipulation in your templates. The downside is that you don't have a template per item, so if you've got a heterogeneous collection, you need to switch in the collection view template code -- bletcherous.
For the second strategy, I've done code that works something like this:
var Goose = Backbone.Model.extend({ });
var Gaggle = Backbone.Collection.extend({
model: Goose;
};
var GaggleView = Backbone.View.extend({
el: $('#gaggle'),
template: _.template($('#gaggle-template').html()),
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
}
};
var g = new Gaggle({id: 69);
g.fetch({success: function(g, response) {
gv = new GaggleView({model: g});
gv.render();
}});
The template code would look something like this:
<script type="text/template" id="gaggle-template">
<ul id="gaggle-list">
<% _.each(gaggle, function(goose) { %>
<li><%- goose.name %></li>
<% }); %>
</ul>
</script>
Note that I use the _ functions (useful!) in the template. I also use the "obj" element, which is captured in the template function. It's probably cheating a bit; passing in {gaggle: [...]} might be nicer, and less dependent on the implementation.
I think when it comes down to it the answer is "There are two ways to do it, and neither one is that great."
The idea of backbone is that view rendering is event driven.
Views attach to Model data change events so that when any data in the model changes the view updates itself for you.
What you're meant to do with collections is manipulate a collection of models at the same time.
I would recommend reading the annotated example.
I've also found this a confusing part of the Backbone framework.
The example Todos code is an example here. It uses 4 classes:
Todo (extends Backbone.Model). This represents a single item to be todone.
TodoList (extends Backbone.Collection). Its "model" property is the Todo class.
TodoView (extends Backbone.View). Its tagName is "li". It uses a template to render a single Todo.
AppView (extends Backbone.View). Its element is the "#todoapp" . Instead of having a "model" property, it uses a global TodoList named "Todos" (it's not clear why...). It binds to the important change events on Todos, and when there's a change, it either adds a single TodoView, or loops through all the Todo instances, adding one TodoView at a time. It doesn't have a single template for rendering; it lets each TodoView render itself, and it has a separate template for rendering the stats area.
It's not really the world's best example for first review. In particular, it doesn't use the Router class to route URLs, nor does it map the model classes to REST resources.
But it seems like the "best practice" might be to keep a view for each member of the collection, and manipulate the DOM elements created by those views directly.