7

I have setup my application to use the ember routing architecture. My index pages looks like this (for simplicity sake)

script(type='text/x-handlebars', data-template-name='application')
    div.container
        {{outlet}}

and my ember app like this

window.App = Em.Application.create({
    ApplicationController: Em.Controller.extend(),
    ApplicationView: Ember.View.extend({
    templateName: 'application'
    }),
    Router: Em.Router.extend({
        root: Em.Route.extend({
        doHome: (router, event) ->
            router.transitionTo('home')
        doInbox: (router, event) ->
            router.transitionTo('inbox')
        doInboxModal: (router, event) ->
            $("#inbox").modal "show"
        home: Em.Route.extend({
            route: '/',
            connectOutlets: (router, event) ->
                router.get('applicationController').connectOutlet('home')   
        }),
        inbox:Em.Route.extend({
            route: '/inbox',
            connectOutlets: (router, event) ->
                router.get('applicationController').connectOutlet('inbox')  
        })
    })  
})

I have the home and inbox working fine, but first of all, I am doing jquery in my doInboxModal to show the modal inbox. And if I want to then have a button on the modal inbox to go to the actual inbox page, it won't work.

So, the question is, how do I properly use a Twitter Bootstrap Modal with ember routing?

CraigTeegarden
  • 8,173
  • 8
  • 38
  • 43
WallMobile
  • 1,939
  • 1
  • 20
  • 35
  • I also posted a simple example of a [Twitter Bootstrap modal example in Ember.js](http://stackoverflow.com/questions/16879046/how-to-create-and-manage-a-twitter-bootstrap-modal-with-ember-js) – Robert Carter Mills Jun 02 '13 at 02:09

3 Answers3

7

When you route to a view, call the modal in the didInsertElement, which will load the modal..Assuming you want the modal to load on the inbox view

App.InboxView = Ember.View.extend({
  didInsertElement: function(){
    $("#my-modal").modal("show");
  }
})

your updated router:

window.App = Em.Application.create({
  ApplicationController: Em.Controller.extend(),
  ApplicationView: Ember.View.extend({
    templateName: 'application'
  }),
  Router: Em.Router.extend({
    root: Em.Route.extend({
      doHome: (router, event) ->
        router.transitionTo('home')
      doInbox: (router, event) ->
        router.transitionTo('inbox')
      home: Em.Route.extend({
        route: '/',
        connectOutlets: (router, event) ->
          router.get('applicationController').connectOutlet('home')   
      }),
      inbox:Em.Route.extend({
        route: '/inbox',
        connectOutlets: (router, event) ->
          router.get('applicationController').connectOutlet('inbox')  
      })
  })  
})

Hope this helps...


Updated Answer
App.InboxView = Ember.View.extend({
  templateName: "inbox",
  addNewEmail: function(){
    $("#my-modal").modal("show");
  },
  cancelNewEmail: function(){
    $("#my-modal").modal("hide");
  }
})

inbox.handlebars

<div id="inbox-container">
  <!-- 
    YOUR INBOX CONTENT
    The modal declared below wont show up unless invoked
  -->
  <a {{action addNewEmail}}>New Email</a>
  <a {{action cancelNewEmail}}>Cancel</a>
  <div class="modal hide fade in" id="my-modal">
    <!--
      Put your modal content
    -->
  </div>
</div>

This way:

  • The modal will show on button click
  • view behind doesn't go away
  • The modal will hide on cancel button
Community
  • 1
  • 1
Mudassir Ali
  • 7,913
  • 4
  • 32
  • 60
  • I was assuming he wanted it to be separate from the inbox view actually... in which case there should probably be a `App.InboxModalView` with a `didInsertElement` callback like that one. (You may also need a `willDestroyElement` to hide it.) Finally, an `inboxModal` route to connect it, *inside* the `inbox` route. (I think it could be an `Ember.State` if you don't want a route per se.) – dechov Sep 25 '12 at 18:48
  • Forgot to mention -- you'd need an outlet inside the inbox view, to plop the modal into. – dechov Sep 25 '12 at 18:55
  • yeah I wanted to give him the idea of implementation...and we should hide it in willDestroyElement, using outlet is optional I guess, as it is a modal which comes on the top of everything, but If you need an url for the modal then we should use outlet, – Mudassir Ali Sep 26 '12 at 05:03
  • Thanks, the didInsertElemnt was def what I was looking for. Now, last question on it. Two things, first is, when the modal shows up, I dont want the view behind it to go away. And two, when I have that modal box up, I have a Cancel button on it, what would be the ideal thing for the cancel button to do, call the App.InboxView with a cancelModal function or should that be on the Router? – WallMobile Sep 27 '12 at 15:57
  • When I display one of my modals (it is a breif version of the inbox, with a button to go to the full page) I have this Go to Inbox in there, but I can't get it to call the router. What am I doing incorrectly in there to call the router, or namely, how do I explicitly call the router from a modal or anywhere. – WallMobile Sep 28 '12 at 05:49
  • you might just need to keep it as {{action doInbox}} (No target no inverted commas), this would look for the state doInbox in router & goes to that state, Make sure you define doInbox state in router – Mudassir Ali Sep 28 '12 at 05:53
  • actually I had to set it to this Go to Inbox and it works. Does the I guess an instance variable of router is create even though I just do a .extend – WallMobile Sep 28 '12 at 05:58
  • I don't think you need to do it that way, It may work but I am not sure about the side effects...As per the emberjs documentation http://emberjs.com/guides/outlets/ {{action doInbox}} would suffice which does the transisition to the state defined in router doInbox – Mudassir Ali Sep 28 '12 at 06:02
  • yea, I would think that it would default to the router as well. I just haven't been able to get it to do so. This modal stuff ends up being a bit funky, the other parts of the router I love. – WallMobile Sep 28 '12 at 06:10
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/17280/discussion-between-unspecified-and-wallmobile) – Mudassir Ali Sep 28 '12 at 06:21
5

I have post on using Twitter Bootstrap modals with Ember.js if anyone wants further reference:

http://generali.st/en/site/topics/show/82-modal-forms-in-emberjs

Includes a working JSBin for the full source. Also has some strategies for DRYing up forms. IMO, the naming of things is a bit more conventional as well.

mcginniwa
  • 360
  • 3
  • 9
4

I spent some time dissecting Discourse to learn how they're doing it. Basically they have a singleton modal and the router handles events to display and hide the modal.

Here are the interesting bits :

discourse/app/assets/javascripts/discourse/routes/discourse_route.js

  showModal: function(router, name, model) {
    router.controllerFor('modal').set('modalClass', null);
    router.render(name, {into: 'modal', outlet: 'modalBody'});
    var controller = router.controllerFor(name);
    if (controller) {
      if (model) {
        controller.set('model', model);
      }
      controller.set('flashMessage', null);
    }
  }

discourse/app/assets/javascripts/discourse/routes/application_route.js

  events: {
    editCategory: function(category) {
      Discourse.Route.showModal(router, 'editCategory', category);
      router.controllerFor('editCategory').set('selectedTab', 'general');
  }

discourse/app/assets/javascripts/discourse/views/modal/modal_body_view.js

 Discourse.ModalBodyView = Discourse.View.extend({

  // Focus on first element
  didInsertElement: function() {
    $('#discourse-modal').modal('show');

    var controller = this.get('controller');
    $('#discourse-modal').on('hide.discourse', function() {
      controller.send('closeModal');
    });

    $('#modal-alert').hide();

    var modalBodyView = this;
    Em.run.schedule('afterRender', function() {
      modalBodyView.$('input:first').focus();
    });

    var title = this.get('title');
    if (title) {
      this.set('controller.controllers.modal.title', title);
    }
  },

  willDestroyElement: function() {
    $('#discourse-modal').off('hide.discourse');
  }

 });

discourse/app/assets/javascripts/discourse/mixins/modal_functionality.js

 Discourse.ModalFunctionality = Em.Mixin.create({
  needs: ['modal'],

  /**
    Flash a message at the top of the modal

    @method blank
    @param {String} name the name of the property we want to check
    @return {Boolean}
  **/
  flash: function(message, messageClass) {
    this.set('flashMessage', Em.Object.create({
      message: message,
      messageClass: messageClass
    }));
  }

 });

app/assets/javascripts/discourse/templates/modal/modal.js.handlebars

 <div class="modal-header">
   <a class="close" {{action closeModal}}><i class='icon-remove icon'></i></a>
   <h3>{{title}}</h3>
 </div>
 <div id='modal-alert'></div>

 {{outlet modalBody}}

 {{#each errors}}
   <div class="alert alert-error">
     <button class="close" data-dismiss="alert">×</button>
     {{this}}
   </div>
 {{/each}}

And in their application.js.handlebars : {{render modal}}

Ugo Méda
  • 1,205
  • 8
  • 23