how to avoid my method overriding existing backbone view methods - backbone.js

would adding event methods override the existing backbone view methods. I am using layout manager which add some more methods..I wanted to avoid override the methods..what is the best way for naming my method
$ ->
class Overlay extends Backbone.View
events:
'click .close': 'close'
close: (e)=>
#remove()
The close Method will be added to the view..if there is a close method in backbone view it will be overridden...??

yes, same reason how implementing your own render function will overwrite backbone's default render function.
You can always prefix your methods with underscore if you are worried that you might overwrite some default functions.
Example:
'click .close': '_close'
It's a common practice to prefix private methods with underscore anyways. That or just give it a more specific name... You can also define the function directly:
'click .close': function(){
...
}

Related

Prevent itemView from being added to CompositeView after collection "add"

I have a problem with Backbone and Marionette. I have a CompositeView with a collection where people can a comment, this all works nicely, the comment is added and saved to the server but I don't want the view to update and to show the newly added comment. I have tried this:
App.Views.CommentsView = Backbone.Marionette.CompositeView.extend({
template: '#article-comment-container',
itemViewContainer: 'ul',
itemView: App.Views.CommentView,
collectionEvents: {
"add": "modelAdded"
},
modelAdded: function(){
console.log('Please do nothing!');
}
});
But the item is still rendered into the page on top of my modelAdded function being called. Can I prevent that from happening at some point?
In a different scenario I would like new items to be added to the top of the list and not the bottom. Do I have to override the entire appendHtml method achieve this?
Setting the collection event add simply adds another handler to the queue for that event; it doesn't replace any other events so the default marionette behaviour will still occur.
I assume you're calling the create method on the collection to create your new comment model. If this is the case you simply need to set the silent option to true. Now the add event will not fire and Marionette will not create and render the view for that model. You can do it like this:
commentCollection.create(commentModel, {silent: true});
As for you second question about prepending, yes I would override appendHtml method. Or to keep the method names consistent with what actually happens, create a method called prependHtml and then override the renderItemView method to call prependHtml.

backbone.js scroll event with handler is not unbinding

I've binded window's scroll event to a view's method like:
MyView = Backbone.View.extend({
initialize: function(){
_.bindAll(this, 'handleScrolling');
$(window).off('scroll', this.handleScrolling).on('scroll', this.handleScrolling);
}
})
I see this is not working. If this callback is triggered as many times as this view is instantiated. However, if I remove handler from off, then it is correctly unbinding and triggers only once per scrolling. Like:
$(window).off('scroll').on('scroll', this.handleScrolling);
Any idea why this is happening? I dont want to remove all callbacks from this event as other views/codes may bind event to it which will make app behaving unexpected.
Is there any better way of binding events to window/document or other element outside the scope of current view?
Your problem is right here:
_.bindAll(this, 'handleScrolling');
That's equivalent to:
this.handleScrolling = _.bind(this.handleScrolling, this);
so each time you instantiate your view, you're working with a brand new function in this.handleScrolling. Then you do this:
$(window).off('scroll', this.handleScrolling)
But that won't do anything since the this.handleScrolling function that you attached with on:
.on('scroll', this.handleScrolling);
isn't the same function as the this.handleScrolling function that you're trying to .off. The result is that each time you create a new instance of your view, you're leaving the old scroll handler in place and adding a new one.
The proper solution (IMO) is to add a remove method to properly clean things up:
remove: function() {
$(window).off('scroll', this.handleScrolling);
return Backbone.View.prototype.remove.apply(this);
}
and then call view.remove() before creating the new view.
It looks like you have a new instance of the handler this.handleScrolling in each call.
so when jQuery tries to remove the specific handler it will not find the handler in the event registry, so it will not be able to remove it.
Problem: Demo
I would suggest using event namespaces here
$(window).off('scroll.myview').on('scroll.myview', this.handleScrolling);
Demo: Fiddle
Another solution is to use a shared handler like this

What is the best practice to use a jQuery Plugin with Backbone.js

I am using jQuery UI with a rails application using backbone.js. I want to make a draggable element? Where do I have to put this function :
$('.area-tools').draggable({handle: ".grap-area", "containment" : "parent"})
Is it on the view? After the render function? Because, the initialize function doesn't find my element, I think the DOM is not already created?
So, i did this :
class Myapp.Views.Tools extends Backbone.View
template: JST['pdfs/tools']
tagName: "div"
className: "pdf-tools"
events:
'click div.rect' : 'drawRect'
initialize: ->
#previewWrapper = $('.preview')
#count = 0;
#
render: ->
$(#el).html(#template())
#initColorPicker()
this
initColorPicker: ->
$('.area-tools').draggable({handle: ".grap-area", "containment" : "parent"})
drawRect: (event) =>
newElement = $('<div id="resizable" class="resizable"><div class="close">x</div><input type="text" name="text_' + #count++ + '" /></div>');
#previewWrapper.append(newElement);
newElement.draggable().resizable();
Is it good? Any recommendation?
I just had the same issue come up when integrating the timeago plugin into my rails/backbone.js app.
My solution was almost the same as yours, except that instead of applying the plugin to the entire document, I apply it just to the view element. i.e. add a this before your selector:
initColorPicker: ->
#.$('.area-tools').draggable({handle: ".grap-area", "containment" : "parent"})
That keeps the range of what you're doing with the plugin confined to the specific view you call the plugin from, which is important.
Your analysis is correct, that your code will not work unless your view is attached to the Dom. You do have multiple options now:
Move the event handling into the View via the events option. I would then also recommend to attach the event handlers to your view.
Attach the view to the dom during creation via the el option. Read this post, especially the section on "Decouple Views from other DOM elements"
Following shioyama's recommendation, is the 3rd option to fix your problem.
All 3 of those should fix your problem. But all 3 are best practice, so you might want to apply all 3.

Backbone Marionette - Add a visual effect when switching view

Is there a convenient way to add an effect when I leave a page (close a view/layout) and open a new one in the same region ? (something like a fade effect)
Marionette regions have a method called open that by default just replace the HTML of the old view with the new view. You can override this method to do any animation you like. From the region documentation:
Set How View's el Is Attached
If you need to change how the view is attached to the DOM when
showing a view via a region, override the open method of the
region. This method receives one parameter - the view to show.
The default implementation of open is:
Marionette.Region.prototype.open = function(view){
this.$el.html(view.el);
}
This will replace the contents of the region with the view's
el / content. You can change to this be anything you wish,
though, facilitating transition effects and more.
Marionette.Region.prototype.open = function(view){
this.$el.hide();
this.$el.html(view.el);
this.$el.slideDown("fast");
}
This example will cause a view to slide down from the top
of the region, instead of just appearing in place.
You could override the close function on the view, doing something like this:
close: function () {
// fancy fade-out effects
Backbone.Marionette.View.prototype.close.apply(this, arguments);
}
And do something similar with your render functions.
This seems to work for me:
this.views = {
messageItem: Marionette.ItemView.extend({
template: Handlebars.templates.messaging_item,
tagName: "li",
className: "messaging-item",
render: function(){
this.$el.html(this.template(this.model.attributes));
this.$el.hide();
},
onShow: function(){
this.$el.slideDown(800);
}
})
};
For future users people could user my plugin for Transition Support in marionette.
https://github.com/saqibshakil/Marionette.TransitionRegion/
I used css3 transitions as those have more hardware support than jquery animations. on the downside using this makes the code async so be carefull of that.
I think this could be useful for you.
The following marionette plugin that adds 4 kind of transitions. There can be easily added more transition types.
Basically instead of using yourRegion.show(view)...
you can use now yourRegion.showAnimated(view, {animationType: 'yourAnimation'});
it's very easy to use.
https://github.com/marcinkrysiak1979/marionette.showAnimated
see the documentation on github for more info

Backbone boilerplate Events

What is the best way to bind events to a Backbone boilerplate application? I've been trying to bind my events directly to the models associated with my views, in my views, but it doesn't seem to be working. I see within 'namespace.js', that there is an app key that extends Backbone.Events like so:
// Keep active application instances namespaced under an app object.
app: _.extend({}, Backbone.Events)
I don't fully understand how to use it...
I was able to get things working without the boilerplate, but it does provide some very cool functionality, so I'd love to be able to use it. Thanks!
ADDED
the code I was using was with the underscore bind method like so:
this.module.bind('change', this.render);
But then, I realized that 'this.model' is returning undefined, and so this doesn't work. I really am not sure how the boilerplate wants me to reference my model from the view.
I'm not sure if it is a typo that you copied from your code or a typo you only entered here, but I believe this.module (which IS undefined) should be this.model, which you also must be sure to pass in when you instantiate your view, of course, as so:
myView = new BBView({model: myModel});
then you can say this.model.bind('change', this.render); or this.model.on('change', this.render); for the most recent version of Backbone
I frequently bind my views to change events on my models in this way.
As for extending Backbone.Events, the way I have used it is to create an independent "event aggregator" that will help connect events between different views on your page. Let's say for example you have some action on one view that needs to set off an action on another view. In this case, you can pass your event aggregator object as an option to each of your views when you instantiate them, so that they can both trigger events or bind to events on a common object (i.e. your event aggregator).
whatsUp = _.extend({}, Backbone.Events) // the aggregator
myFirstView = new FirstBBView ({whatsUp: whatsUp});
(the aggregator shows up as this.options.whatsUp inside the view)
mySecondView = new SecondBBView2 ({whatsUp: whatsUp});
inside FirstBBView:
this.options.whatsUp.bind('specialEvent', function(arg1,arg2) {
// do stuff
});
inside SecondBBView, when something important happens:
this.options.whatsUp.trigger('specialEvent', {arg1: 'some data', arg2: 'more data'});
For a great explanation, see this great article by Derick Bailey

Resources