Ember CLI Bootstrap Modals

It’s really easy to create reusable modal code to integrate Twitter Bootstrap modals into EmberJS projects. Here’s how to do it:

TL;DR

Demo at http://ember-cli-bootstrap-modal.herokuapp.com

Code at https://github.com/mattbeedle/ember-cli-bootstrap-modal-example

The Modal code

The modal code is all the code that our modals will be built on top of. It consists of a ModalController, ModalView, openModal and closeModal actions, and a modal outlet. Links to demo app and working code are at the bottom of the article.

First, let’s create a basic application template with a modal outlet into which our modals will be rendered:

app/templates/application.hbs

<h2 id='title'>Bootstrap Modal with Ember CLI examples</h2>

{{outlet}}

{{outlet "modal"}}

Now we will define a ModalView. All modal views will inherit from this view. It ensures that modal settings are consistent across all modals.

app/views/modal.coffee

`import Ember from 'ember'`

ModalView = Ember.View.extend
  tagName: 'div'
  classNames: 'modal'.w()

  # didInsertElement is run once the element of the view has been inserted into
  # the DOM
  # http://emberjs.com/api/classes/Ember.View.html#event_didInsertElement
  didInsertElement: ->
    # First set the div id to "modal". "@$()" is the shortcut to get the view's
    # jQuery element
    @$().attr('id', 'modal')

    # Now set up the modal
    @$().modal
      keyboard: false
      backdrop: 'static'

    # Now show the modal
    @$().modal('show')

  # This is run when the element of the view is going to be destroyed. We
  # override it here to hide the modal first.
  # http://emberjs.com/api/classes/Ember.View.html#event_willDestroyElement
  willDestroyElement: ->
    @$().modal 'hide'

`export default ModalView`

Now we define a ModalController. This will handle the cancel action. All other modal controllers will inherit from this.

app/controllers/modal.coffee

`import Ember from 'ember'`

ModalController = Ember.ObjectController.extend
  actions:
    cancel: ->
      # If the controller @content is set, then we need to roll it back to avoid
      # inconsistent state between client and server.
      @content.rollback() if @content

      # Now we send "closeModal" event to bubble up to the application route
      @send 'closeModal'

`export default ModalController`

Now we need to define the global openModal and closeModal actions. We will do this in ApplicationRoute as everything bubbles up to here in the end.

app/routes/application.coffee

`import Ember from 'ember'`

ApplicationRoute = Ember.Route.extend
  actions:
    # This takes one argument; The template to render. It then renders it into
    # The modal outlet which we defined in the application template.
    openModal: (modal) ->
      @render modal,
        into: 'application'
        outlet: 'modal'

    # This handles removing the modal. It simply disconnects the modal outlet to
    # remove the modal. When this happens the "willDestroyElement" function will
    # be called on the ModalView, telling it to close the modal.
    closeModal: ->
      @disconnectOutlet
        outlet: 'modal'
        parentView: 'application'

`export default ApplicationRoute`

Building our first modal

At this point all of the code to handle modals is complete. Now we can actually create our first modal. Let’s do this with an action fired from our index template.

app/templates/index.hbs

<p><a href="" {{action openConfirmationModal on="click"}}>Confirmation Modal</a></p>

We will create an IndexRoute to handle the openConfirmationModal action.

app/routes/index.coffee

`import Ember from 'ember'`

IndexRoute = Ember.Route.extend
  actions:
    openConfirmationModal: ->
      # The "openModal" event will bubble up to the ApplicationRoute and be
      # handled there.
      @send 'openModal', 'confirmation/new'

`export default IndexRoute`

We will need a confirmation/new template now to be rendered.

app/templates/confirmation/new.hbs

<div class="modal-dialog">
  <div class="modal-content">
    <div class="modal-header">
      <button class="close" type="button" {{action cancel on="click"}}>x</button>
      <h4 class="modal-title">Confirm</h4>
    </div>
    <div class="modal-body">
      Are you sure about this?!?!
    </div>
    <div class="modal-footer">
      <button class="btn btn-default" {{action cancel on="click"}}>Nope</a>
      <button class="btn btn-danger" {{action confirm on="click"}}>Yes, do it!</a>
    </div>
  </div>
</div>

Now we will need to create a ConfirmationNewController to handle the events from the confirmation/new template.

app/controllers/confirmation/new.coffee

`import ModalController from 'ember-cli-bootstrap-modal-example/controllers/modal'`

# We inherit from ModalController to get the "cancel" action.
ConfirmationNewController = ModalController.extend
  actions:
    confirm: ->
      alert 'OK, it will be done!'
      @send 'closeModal'

`export default ConfirmationNewController`

Finall we must create a ConfirmationNewView that extends ModalView to get all of the modal view functionality.

app/views/confirmation/new.coffee

`import ModalView from 'ember-cli-bootstrap-modal-example/views/modal'`

ConfirmationNewView = ModalView.extend()

`export default ConfirmationNewView`

Notes & links

So that’s it. Simple Bootstrap modals in EmberJS. I’ve created a demo app and published it to GitHub. It includes a confirm modal, information modal and a form modal (using ember-easyForm, ember-validations and ember-data LocalStorage).

Demo at http://ember-cli-bootstrap-modal.herokuapp.com

Code at https://github.com/mattbeedle/ember-cli-bootstrap-modal-example

Need some extra development power?

I don't have a lot of time at the moment, but I'm always interested to hear about new projects. I'm particularly interested in EmberJS/Elixir projects and refactoring Rails monoliths. Drop me a line at hello [at] mattbeedle [dot] name or using my contact form http://www.mattbeedle.name/#page-contact.

Hire Me


comments powered by Disqus
comments powered by Disqus