How to modelEvents work in Marionette.CompositeView - javascript

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:

Related

Auto re-render in Ampersand.js

I have a main app view, which has 2 subviews. Each subview has its own collection passed to it.
One of these collections is being updated during the user journey. But the subview associated with it is not updating.
How can I make the view re-render once the collection is updated?
As I understand, you are rendering several items, each of which corresponds to one model in your collection. In this case, to take advantage of dynamic inserting/removing of corresponding items, you should use renderCollection like this in your subview:
render: function(){
this.renderWithTemplate(this);
this.renderCollection(this.collection, ItemView, this.el.querySelector('SELECTOR'));
return this;
}
Here ItemView is the view which corresponds to one model in the collection (it's the constructor, not instantiated view). From this view you have access to this.model and in this view you can define any bindings and events you might want. For more details check out official documentation.

backbone.js correct way to update model when view changed

What is a correct way to update model when view changed. Is it need to do in a view or in some other place like controller or so?
for example I have a model (model1) and some view (View1) associated with this model. I need to update model when I change some fields on the view. Which is correct way to do that?
Also, I have a collection of that models associated with another view (View2), and i need to put model (model1) to that collection.
The second question - should View1 or Model1 know about collection, and if yes - how it should be done?
There is interesting approach of MVC theory, applied to MVC... Look here for more explaination what is controller in Backbone
http://lostechies.com/derickbailey/2011/12/23/backbone-js-is-not-an-mvc-framework/
written by #derick-bailey
In general summary, Bailey says that there are no controllers in Backbone. So, I personally think, that every model updates should take place in the View, associated with that model.
Every model in Backbone, when is added to a collection ( or collections ) and is updated triggers an event in the collection(s) it belongs, so you should listen for that event in your view.
In your example
View1.collection = [model1];
View2.collection = [model1, model2, model3];
And when View1 changes model1, then model1 triggers an event to View2.collection, which you can listen.
Controllers talk to models, so yes: the correct way to update your model when the view changed is through your controller

BackboneJs - Should model or Collection have knowledge of view

In one of the example i picked from SO answers here and many BackBoneJs examples i see that the initialize function knows which view the model is going to be rendered with. I don't know i am kind of biased now, is this a good practice or it depends on type of application being developed.
Example
http://jsfiddle.net/thomas/Yqk5A/
Edited Fiddle
http://jsfiddle.net/Yqk5A/187/
Code Reference
FriendList = Backbone.Collection.extend({
initialize: function(){
this.bind("add", function( model ){
alert("hey");
view.render( model );
})
}
});
Is the above a good practice or below
var friendslist = new FriendList;
var view = new FriendView({el: 'body'});
friendslist.bind("add", function( model ){
alert("hey" + model.get("name"));
view.render( model );
})
in the edited fiddle collection is rendered by a view, and we also can use many more views to render the collection.
I am all for using events,
I myself don't want to move the bind's outside the model, I'd keep them there like the original example
var app = {};
app.evt = _.extend({}, Backbone.Events); // adding a global event aggregator
FriendList = Backbone.Collection.extend({
initialize: function(){
this.bind("add", function( model ){
alert("hey");
app.evt.trigger('addfriend', model);
})
}
});
//further in your code you can bind to that event
app.evt.bind("addfriend", function(model){
var view = new FriendView({el: 'body'});
view.render(model);
});
however, i find the example a bit weird, creating a new view, with body as element, and rendering it with giving a model to the render function. i'd find it more logic if the view is created with a model as attribute, and then rendering the content into the body. but thats another subject all together.
in short, i move creating the view outside, listening to an event being triggered, but the bind on the collection stays in the collection code. i find it more managable to keep all the collection code at the same place.
I don't think the collection should know about the view that is used to render it. I know in my projects, the same collection is render in multiple ways so that approach would deteriorate rapidly.
In general I pass the collection to the view that renders the collection and the view will listen to add/remove/update events of the collection to render the elements. The collection view will have knowledge of the child view.
Check out the following link (3rd blog in a series) and in particular the UpdatingCollectionView. This is the approach that I've found useful.
http://liquidmedia.ca/blog/2011/02/backbone-js-part-3/

Handling multiple instance of a collection?

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.

backbone.js - collections and views

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.

Categories