Using Marionette to Display Modal Views

Using Marionette to Display Modal ViewsFor a while, I’ve been thinking about how best to handle showing modal dialog boxes for my applications while utilizing Backbone views. A lot of interesting ideas passed through my head, but none of them seemed exactly right. Then I saw a post by Derick Bailey where he described how he uses Marionette’s Regions to handle the work. His post is a bit on the old side and Regions have changed a bit since, so I decided to look into how to do it myself.

The Issues

There are several issues surrounding creating modal dialogs just with a view. As Derick talks about in his article, most plugins for creating these dialogs will remove (or just move) the actual element from DOM, so any events that you set up in the view will be lost.

Along with that, we lose reusability. By making the view handle the work of controlling the modal, it can’t be used in a place where you don’t want a modal window. By moving the modal functionality out of the view, it can be used anywhere in the application.

The Solution

Now that we know we need to pull the modal functionality out of the views, it’s just a matter or figuring out where to put it. Marionette’s Regions are perfect for this. Regions are objects that represent a place that already exists in the DOM and they handle adding and removing views to/from that place in the DOM. Simply call show on a Region to add a view there and call close to remove it.

All we need to do is augment a Region to call the plugin’s method for showing the modal when the view is shown, make sure to hide the modal when the view is closed, and close the view when the modal is hidden.

Here I’ve developed a ModalRegion that uses Twitter Bootstrap’s modal plugin:

var ModalRegion = Marionette.Region.extend({
    constructor: function() {
        Marionette.Region.prototype.constructor.apply(this, arguments);

        this.ensureEl();
        this.$el.on('hidden', {region:this}, function(event) {
            event.data.region.close();
        });
    },

    onShow: function() {
        this.$el.modal('show');
    },

    onClose: function() {
        this.$el.modal('hide');
    }
});

There are a few things worth noting here:

  • I created a `constructor` function instead of `initialize`. This is because if someone extends this Region, they’ll override the `initialize` function with theirs. This way, it won’t be overridden.
  • I call `this.ensureEl()`. This is a Region’s method for caching the jQuery object for the Regions element to `this.$el`. Normally, this method is called in its `show` method, but we needed `this.$el` to be set up before that.
  • There’s some strange things happening in that event handler:
    • The ‘hidden’ event is fired by Twitter Bootstrap after it hides the modal.
    • The object that is passed in as the second parameter to `on` is a data object that is attached to `event` as its `data` property. This isn’t commonly used, so many people don’t know it exists. If this is new to you, check out the API. I passed in this object as a way to make sure that the `close` method was called on the region with the right context. There are several alternative ways to do this, and this probably isn’t the best way, but it avoids a closure and it helps me to teach you something you not have known about before.
    • `onShow` and `onClose` are called immediately after the `show` and `close` methods are finished. This is a simple way for us to augment the `show` and `close` methods to do more than they normally do without overriding the functions.

Now it’s simple to use:

var modal = new ModalRegion({el:'#modal'});
var view = new SomeView();

modal.show(view);

Conclusion

As simple as that. This can be changed pretty easily to use other plugins such as jQueryUI, KendoUI, Wijmo, etc. Also, now that the modal functionality is in one place, if you end up switching the plugin that you use, you only have to change the code in this region and everything should work fine. The only thing you’ll need to work at is getting all the modal-specific markup in there. God bless and happy coding.

About the Author

Author: Joe Zim

Joe Zim

Joe Zimmerman has been doing web development ever since he found an HTML book on his dad's shelf when he was 12. Since then, JavaScript has grown in popularity and he has become passionate about it. He also loves to teach others though his blog and other popular blogs. When he's not writing code, he's spending time with his wife and children and leading them in God's Word.


  • Will Mruzek

    I like the solution you’ve laid out here, but I haven’t been able to get the ‘hidden’ event to work. Any idea why?

    • http://www.joezimjs.com Joe Zimmerman

      I’ll take a look when I get time. In the meantime, if you could send me the exact code you’re using so I can be sure you’re not making a mistake, that would help. Post a link to a Github repo, or use my contact form to get in touch with me.

      • Will Mruzek

        Thanks for your reply, Joe.

        I found that the in order in which I was loading my scripts was causing the listener for ‘hidden’ to be bound in way that wasn’t catching any of the Bootstrap events.

        It’s all working now!

        • http://www.joezimjs.com Joe Zimmerman

          Great! I’m glad you figured it out. It’s always some tiny little thing like that.

  • Sooraj V Nair

    Great solution.