18

The 2nd answer to this question nicely explains how event declarations in Backbone.js views are scoped to the view's el element.

It seems like a reasonable use case to want to bind an event to an element outside the scope of el, e.g. a button on a different part of the page.

What is the best way of achieving this?

Community
  • 1
  • 1
William
  • 13,332
  • 13
  • 60
  • 73
  • 1
    It seems unreasonable to me that a view would be responsible for listening to events *outside* of its concern. Now, if you mean a "global" type of view (say that draws sub-views), then Backbone/jQuery's delegation continues to work for this button you speak of. – Roatin Marth Nov 25 '11 at 21:25
  • I'm probably misunderstanding things (my first day of using Backbone) - but as a simple example I'm imagining a view of list items (a div containing a
      ) but the form to add a new list item being outside of that div.
    – William Nov 25 '11 at 21:43
  • @WilliamLannen If you want to handle the event of a global app button that adds a new item, create an View that is the "container" for your application, handling those types of events. That's the approach I've used – Bartek Nov 25 '11 at 21:45

2 Answers2

30

there is not really a reason you would want to bind to an element outside the view, there are other methods for that.

that element is most likely in it's own view, (if not, think about giving it a view!) since it is in it's own view, why don't you just do the binding there, and in the callback Function, use .trigger(); to trigger an event.

subscribe to that event in your current view, and fire the right code when the event is triggered.

take a look at this example in JSFiddle, http://jsfiddle.net/xsvUJ/2/

this is the code used:

var app  = {views: {}};

app.user = Backbone.Model.extend({
    defaults: { name: 'Sander' },
    promptName: function(){
        var newname = prompt("Please may i have your name?:");
        this.set({name: newname});
    }
});

app.views.user = Backbone.View.extend({
    el: '#user',
    initialize: function(){
        _.bindAll(this, "render", "myEventCatcher", "updateName");
        this.model.bind("myEvent", this.myEventCatcher);
        this.model.bind("change:name", this.updateName);
        this.el = $(this.el);
    },
    render: function () {
        $('h1',this.el).html('Welcome,<span class="name">&nbsp;</span>');
        return this;
    },
    updateName: function() {
        var newname = this.model.get('name');
        console.log(this.el, newname);
        $('span.name', this.el).text(newname);
    },
    myEventCatcher: function(e) {
        // event is caught, now do something... lets ask the user for it's name and add it in the view...
        var color = this.el.hasClass('eventHappened') ? 'black' : 'red';
        alert('directly subscribed to a custom event ... changing background color to ' + color);
        this.el.toggleClass('eventHappened');

    }
});

app.views.sidebar = Backbone.View.extend({
    el: '#sidebar',
    events: {
        "click #fireEvent" : "myClickHandler"
    },
    initialize: function(){
        _.bindAll(this, "myClickHandler");
    },
    myClickHandler: function(e) {
        window.user.trigger("myEvent");
        window.user.promptName();
    }
});


$(function(){
    window.user = new app.user({name: "sander houttekier"});
    var userView = new app.views.user({model: window.user}).render();
    var sidebarView = new app.views.sidebar({});
});
Sander
  • 13,301
  • 15
  • 72
  • 97
  • The event aggregator described in this post seems like a great option too: http://lostechies.com/derickbailey/2011/07/19/references-routing-and-the-event-aggregator-coordinating-views-in-backbone-js/ – William Nov 26 '11 at 15:48
  • that is true, while my example will work, it is likely that in bigger applications you would want to keep your events organised, and an eventAggregator like Derick is talking about does that trick very well. it is however up to you to see if your app needs it, the event's are all the same, you only bind them to 1 object and listen to 1 object instead of binding them to the model itself. both will serve well :) – Sander Nov 26 '11 at 19:43
  • I disagree that you shouldn't need it: there are many cases where a click outside of an element should disable the view (dialogs, edit-modes). Or at least...I disagree unless there's a better way to do it. – Chris Pfohl Jan 02 '13 at 18:52
  • One case i need this behavior is because zurb-foundation's reveal modal appends it to html body instead of el. – user1801879 Jun 05 '16 at 21:47
8

Update: This answer is no longer valid/right. Please see other answers below!

Why do you want to do this?

Apart from that, you could always just bind it using regular jQuery handlers. E.g.

$("#outside-element").click(this.myViewFunction);

IIRC, Backbone.js just uses the regular jQuery handlers, so you're essentially doing the same thing, but breaking the scope :)

Bartek
  • 15,269
  • 2
  • 58
  • 65
  • Accepting this as it directly answers my question - though I now appreciate that this probably isn't a good idea!! – William Nov 26 '11 at 15:24
  • bad idea, never refer to the elements outside of the view. Instead create an outer view, and use it as a "controller" – matteosister Jun 11 '13 at 09:34
  • 2
    Indeed, I've learned alot about Backbone since my answer 2 years ago :D – Bartek Jun 11 '13 at 13:43