Triggering custom events in backbone.js - javascript

I am trying to better structure my events in backbone. Currently I have one view communicating with another by triggering events within a function, and listening for events in another function, like:
//childView
triggerFilterChange: function(e){
Backbone.trigger('filterChange',activeFilters);
}
//parentView
initialize: function(){
Backbone.on('filterChange', this.onFilterChange, this);
},
onFilterChange:function(filters){
//DO STUFF
}
This works, however, I would like to make it more clear in my child view what events should be expected, rather than just triggering something from a random function somewhere buried within the view. For example, all of my click events for the child view are clearly delineated at the top of the page:
define([
'jquery',
'underscore',
'backbone',
], function($, _, Backbone){
var ProductsFiltersView = Backbone.View.extend({
events : {
"click .filter-options-container" : "filterOptionContainerClick",
"click .filter-option" : "filterOptionClick",
},
So my question is, is there a way to put custom events into this events object? Or some structure similar to this, so you know at the top of the view what events might be triggered? Like:
events : {
"click .filter-options-container" : "filterOptionContainerClick",
"click .filter-option" : "filterOptionClick",
"filterChange" : "doFilterChange"
},
doFilterChange: function(){
Backbone.trigger('parentFilterChange',activeFilters);
}

I wouldn't touch the events object for this anyhow.
The event object defines the events from child elements of el that are propagated to the parent element (el).
The question is more about how you glue different views together.
I use intermediary custom objects (which I call controllers, as Backbone does not provide Controllers out of the box) that are used to instantiate the views and listen to events that were triggered from within the view.
You can use specified event aggregators to use within particular 'controller' objects to handle the events.
MyApp = {};
MyApp.some_vent = _.extend({}, Backbone.Events);
MyApp.some_vent.on("some:event", function(){
alert("some event was fired!");
});
If the controller holds a reference to the view where you want to cause some effect, you can execute these view's methods accordingly.

No, it is not possible.
The event hash is parsed and event handlers are set by Backbone.View.prototype.delegateEvents.
delegateEvents uses selectors and method names from events, to bind delegated jQuery (or compatible library, like Zepto) event listeners on the view's DOM element:
if (selector === '') {
this.$el.on(eventName, method);
} else {
this.$el.on(eventName, selector, method);
}
Therefore you cannot use Backbone.View.prototype.event to listen to non-DOM events (or, indeed, DOM events from outside the view's element), without overriding Backbone.View.prototype.delegateEvents with your own implementation.
As an aside, your example code is flawed - triggering filterChange would result in an infinite loop.

Related

How to properly destroy view on backbone?

So Im new at backbone, and Im trying to make a single page app, Im using routes to manage certain things, and I want to remove a view when the user gets to another route
Im using this method to destroy the view
destroy_view: function() {
// COMPLETELY UNBIND THE VIEW
this.undelegateEvents();
this.$el.removeData().unbind();
// Remove view from DOM
this.remove();
Backbone.View.prototype.remove.call(this);
}
also this is my route element
Router = Backbone.Router.extend({
routes: {
'':'index',
'#':'index',
'events/*event' : 'events'
},
index: function(){
this.indexView = new VistaIndex();
},
events: function(params) {
if( this.indexView )
this.indexView.destroy_view()
this.eventView = new EventView({currentEvent: params})
}
});
the problem with this is that if I do this the app crashes, so what do you recommend me to do :)
Here’s how I do it:
Backbone.View.extend({
//some other view stuff here...
destroy: function () {
this.undelegateEvents();
this.$el.removeData().unbind();
this.remove();
//OR
this.$el.empty();
}
});
First we want to make sure we’re removing all delegated events (the ones in the events:{"event selector": "callback"} hash). We do this so we can avoid memory leaks and not have mystery bindings that will fire unexpectedly later on. undelegateEvents() is a Backbone.View prototype function that removes the view’s delegated events. Simple.
Next we want to cleanup any data in the view object that is hanging around and unbind any events that we bound outside the events hash. jQuery provides a removeData() function that allows us to to do that.
You may also have bound event listeners to your view chain unbind() with no arguments to remove all previously-attached event handlers from your $el. this.$el.removeData().unbind();
Now you may want to do one of two things here. You may want to remove your view element completely OR you just want to remove any child elements you’ve appended to it during it’s life. The latter would be appropriate if, for example, you’ve set the $el of your view to be some DOM element that should remain after your view behavior is complete
In the former case, this.remove() will obliterate your view element and it’s children from the DOM.
In the later case, this.$el.empty() will remove all child elements.
Check out this fiddle if you want to fool around with my solution.
http://jsfiddle.net/oakley349/caqLx10x/

What is the difference between this.unbind vs this.$el.unbind in backbone js

I bind some events in backbone view and in this.destoryview() method, i called this.unbind(). But it not unbinding events. When some event happaned it called bounded method twice.
Then i change this.unbind() call by this.$el.unbind(), then it working properly.
events:{
'click #closeButton' : 'clearSearch',
// some events
},
initialize: function(options){
this.container = options.container;
},
render: function() {
if(this.oSearchContext.isAdvancedSearchEnabled() == true)
{
this.$el.html(this.advancedSearchSummaryViewTemplate);
}
else
{
this.$el.html(this.advancedSearchTemplate);
}
this.container.append(this.$el);
},
destroyView method with this.unbind()
destroyView : function()
{
if ( this.oAdvancedSearchSummaryView )
this.oAdvancedSearchSummaryView.destroyView();
if ( this.oAdvancedSearchDetailsView )
this.oAdvancedSearchDetailsView.destroyView();
// unbind all events
this.unbind(); // this.$el.unbind() working perfectly
// empty the rendered element
this.$el.empty();
}
Can you please explain me about differnce between both methods.
You use view.bind (or the modern view.listenTo or view.on) in order to subscribe to another backbone component such as listening to a change event in a backbone model.
You use view.$el.bind (or the modern view.$el.on) in order to listen to user interaction in the DOM.
Same logic applies to unbind or the modern off
Similar syntax and API, different purpose.
Instead of using bind I'd suggest to use Backbone's listenTo:
view.listenTo(model, 'change', view.render);
Once the view is destroyed, all the bindings will be automatically removed (unbinded);

model events are not working in Marionette

I am using Marionette.js for making an Application.
model event was not triggering If I change Attribute value in view.initialize function just like in the following way.
var ChartsView = Marionette.CompositeView.extend({
initialize:function(){
this.model.set({"currentJson":""},{silent:true});
this.model.set({"currentJson":"test"});
},
modelEvents:{
"change:currentJson":"settingOtherAttributesInSameModel"
},
settingOtherAttributesInSameModel:function(){
console.log("This is Testing");
}
});
while instance creation time, I am passing the model object just like in the following way.
chartsViewObj=new ChartsView({el:$('#plannerTablePlace'),model:chartsModelObj});
I don't know, why model event was not working.
I didn't find,where I wrong.
can anyone help me.
Your problem is that the delegateEvents function that bind the view events is called after the initialize function. Thus when you do this :
initialize:function(){
this.model.set({"currentJson":""},{silent:true});
this.model.set({"currentJson":"test"});
}
The attribute is set and the change event is triggered but the view events are not yet bound.
So if you want the events to be triggered, do like this :
chartsViewObj=new ChartsView({el:$('#plannerTablePlace'),model:chartsModelObj});
chartsModelObj.set({"currentJson":""},{silent:true}); // this will not trigger the event because of 'silent:true'
chartsModelObj.set({"currentJson":"test"});

Backbone.js binding a change event to a collection inside a model

I'm pretty new to Backbone so excuse me if this question is a little obvious.
I am having problems with a collection inside of a model. When the collection changes it doesn't register as a change in the model (and doesn't save).
I have set up my model like so:
var Article = Backbone.Model.extend({
defaults: {
"emsID" : $('html').attr('id')
},
initialize: function() {
this.tags = new App.Collections.Tags();
},
url: '/editorial_dev.php/api/1/article/persist.json'
});
This works fine if I update the tags collection and manually save the model:
this.model.tags.add({p : "a"});
this.model.save();
But if the model is not saved the view doesn't notice the change. Can anyone see what I am doing wrong?
initialize: function() {
this.tags = new App.Collections.Tags();
var model = this;
this.tags.bind("change", function() {
model.save();
});
},
Bind to the change event on the inner collection and just manually call .save on your outer model.
This is actually an addendum to #Raynos answer, but it's long enough that I need answer-formatting instead of comment-formatting.
Clearly OP wants to bind to change and add here, but other people may want to bind to destroy as well. There may be other events (I'm not 100% familiar with all of them yet), so binding to all would cover all your bases.
remove also works instead of destroy. Note that both remove and destroy fire when a model is deleted--first destroy, then remove. This propagates up to the collection and reverses order: remove first, then destroy. E.g.
model event : destroy
model event : remove
collection event : destroy
collection event : remove
There's a gotcha with custom event handlers described in this blog post.
Summary: Model-level events should propagate up to their collection, but can be prevented if something handles the event first and calls event.stopPropagation. If the event handler returns false, this is a jQuery shortcut for event.stopPropagation();
event.preventDefault();
Calling this.bind in a model or collection refers to Underscore.js's bind, NOT jQuery/Zepto's. What's the difference? The biggest one I noticed is that you cannot specify multiple events in a single string with space-separation. E.g.
this.bind('event1 event2 ...', ...)
This code looks for the event called event1 event2 .... In jQuery, this would bind the callback to event1, event2, ... If you want to bind a function to multiple events, bind it to all or call bind once for each event. There is an issue filed on github for this, so this one will hopefully change. For now (11/17/2011), be wary of this.

Backbone: Why are events syntax quite different between model and view?

define events in Backbone.Model
var Todo = Backbone.Model.extend({
initialize: function(){
this.on('change', function(){
console.log('- Values for this model have changed.');
});
}
})
define events in Backbone.View
var TodoView = Backbone.View.extend({
className: "document-row",
events: {
"click .icon": "open",
"click .button.delete": "destroy"
}
})
My Question
define events syntax are quite different between model/collection and view, why are they designed in that way?
I think it's better to define model event like this. But backbone don't support it.
var Todo = Backbone.Model.extend({
events: {
"change": "foo"
},
foo: function(){
console.log("test")
}
});
There are two separate type of events: Backbone.Events and jQuery DOM events - so making these look the same seems like a bad idea as it would make code confusing not to mention it wouldn't actually work because the View UI events need different info: '<eventName> <optional DOM selector>: <methodName>' whereas normal internal events have a different syntax.
Backbone.Events follows the typical "publish/subscribe" pattern - it's just a way for apps to internally say "Something has happened" via .trigger and "I want to know when something happens" via .on or .once and the equivalent which you would use in a View because it handles cleaning up the listen when the view is removed: .listenTo and .listenToOnce.
So in your example the "change" event in the model is an internal event which Backbone fires when an attribute changes. The "change" in the View is jQuery DOM event (actually a delegate event) and you could optionally listen to something deeper in the view "change .myinput" so they are not equivalent.
The other difference is that .trigger can pass any arguments it likes after the first one (the event name), whereas the View event passes the DOM event object which you don't control e.g. onChange: function(ev) { ev.preventDefault(); }.

Categories