Question

I have this code http://jsfiddle.net/QRcF5/2/

$(function() {
    var Header = Backbone.Marionette.ItemView.extend({
        template: _.template('<h4>TEST</h4><button>EDIT</button>'),

        tagName: 'nav',

        triggers: {
            'click button': 'btn_clicked'
        }
    }),
    Layout = Backbone.Marionette.Layout.extend({
        template: _.template('<header></header>'),

        regions: {
            header: 'header'
        },

        events: {
            'itemview:btn_clicked': 'clicked'
        },

        clicked: function() {
            alert('Ana are mere');
        }
    });

    layout = new Layout();
    $('body').append(layout.render().el);
    layout.header.show(new Header());
});

I'm using a layout with one ItemView (later I want a more complex nesting) and want to capture in LAYOUT the event that is happening in the ITEMVIEW. It seems its not bubbling upwards or I'm doing something wrong here.


UPDATE 1 I've tried events, triggers (for the child) and on, events (for the parent) but I still can't get the event caught inside the parent.

I can use:

_.extend(App.vent, Backbone.Events);

Backbone.Marionette.ItemView.extend({
    ....
    App.vent.trigger('btn_clicked');
    ....
});

Backbone.Marionette.Layout.extend({
    ....
    App.vent.on('btn_clicked', function() { doCoding(); });
    ....
});

But this way I'm breaking the encapsulation. Is there a way to keep the encapsulation by using Marionette triggers eventing and keep it all in the layout and not polluting the entire app ?


UPDATE 2

http://jsfiddle.net/QRcF5/5/

I've update the jsfiddle to use the _.extend(vent, Backbone.Events). If anyone has a better pattern for this, please let us know :D

Was it helpful?

Solution

I've found the solution (buried somewhere in the documentation making some connections between layout, region and view) !

http://jsfiddle.net/QRcF5/6/


In the context of communication ONLY between the the parent (layout) and the child (itemView):

1) the child triggers the custom event (opposed to vent extend Backbone.Events - it only speaks to those who have access to it = encapsulation)

triggers: {
    'click button': 'custom_event:btn_clicked'
}


2) the parent listen to this event having access to the child with the on('show',....)

initialize: function() {
    var that = this; // needed for the listenTo which overrides 'THIS'

    this.header.on('show', function(view) {
        this.listenTo(view, 'custom_event:btn_clicked', that.clicked);
    });
},


Example:

layout = new Layout();
$('body').append(layout.render().el);
layout.header.show(new Header()); // Here we get access to child and its events


The (hidden) power of Marionette. (I mean when I say hidden, because being relatively new, the documentation is barebone and the complex examples are scarce).

With little perseverance anything can be accomplished.

OTHER TIPS

you should just return true and let the click bubble up in the DOM, see http://jsfiddle.net/aGaDY/ :

var Header = Backbone.Marionette.ItemView.extend({
  template: _.template('<h4>TEST</h4><button>EDIT</button>'),
  events: {
    'click button': 'btn'
  },
  btn: function () {
    return true;
  }
}),

Layout = Backbone.Marionette.Layout.extend({
    template: _.template('<div>Before:</div><header></header><div>After:</div><div class="body">Body Goes Here</div>'),
    regions: {
        header: 'header'
    },
    events: {
        'click': 'btn_clicked'
    },
    btn_clicked: function () {
        alert('Ana are mere');
    }
});
layout = new Layout();
$('body').append(layout.render().el);
layout.header.show(new Header());

You can even remove the event handling on the ItemView completly, bubbling is the default behavior

EDIT:

If you want to be able to distinguish between different buttons click and prevent from polluting the global space with inner events that does not concern the application I can think of two options.

Move the data on the event target via jquery data function and event.target:

// In Header
bth:function(e){
  $(e.target).data("source", "myButton")
}
// ....
// in the Layout 
btn_click: function(e){
  if ($(e.target).data("source") === "myButton") this.doSomething()
}

Another option is that the layout will pass to Header a local backbone.Event extended object so communication will be private between the two.

I understand the issue of encapsulation, but I think that readability will be better in the global App.Vent that you suggested

events: {
            'itemview:btn': 'btn_clicked'
        },

The itemview is not the thing being clicked. You need to specify an actual dom element like this:

events: {
            'click .someThingWithAClass': 'myFunction'
        },

However, if you want to listen to events that happen further down the track, you should probably just stick to the jquery on method. This will listen to anything that bubbles its way up, regardless of the ui that has been generated at the time that this view is rendered

http://api.jquery.com/on/

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top