Bootstrap navbar collapse

I have a bootstrap collapsible navbar menu which works just fine except that I’d like to have it close backup when the user clicks an item.

<ul class='nav navbar-nav navbar-right collapse navbar-collapse'>
    <li>{{#link-to 'info'}}{{fa-icon 'info-circle'}} Info{{/link-to}}</li>
    ...
</ul>

Clicking on the navbar-collapse icon works as expected.

I’ve tried a number of so-called fixes, but none of them seem to work together with ember.

var App = Ember.Application.extend({
    ...
    ready: function() {
        $('.nav li a').on('click', function(){
            $(".navbar-toggle").click();
        });
    }
});

Is there a standard way to get this working with ember?

Personally, I have used the following in the main view:

var ApplicationView = Ember.View.extend({
  didInsertElement: function() {
    // bootstrappy stuff here
  }
});
1 Like

Works like a charm, thanks!

For those interested I’d like to share the solution which works for me.

  • get all items on the navigation bar
  • only trigger click if toggle is visible
  • ignore dropdown menu items
// app/views/application.js
export default Ember.View.extend({
    didInsertElement: function() {
        // Bootstrap collapsible navigation bar
        $('.nav li a').on('click', function(){
            var toggle = $('.navbar-toggle');
            // Only click if toggle is visible and ...
            if (toggle && toggle.is(':visible') && \
                // ... menu item is not a dropdown toggle.
                !$(this).hasClass('dropdown-toggle')) {
                    toggle.trigger("click");
            }
        });
    }});
1 Like

Just a note on doing jquery stuff, it’s a good idea to cleanup, see clear and destroy events. Also it seems to be best practice to wrap your call back stuff in an Ember.run so it runs in the Ember run loop.

// app/views/application.js
export default Ember.View.extend({
    didInsertElement: function() {
        // Bootstrap collapsible navigation bar
        $('.nav li a').on('click', function(){
            Ember.run(function(){
                var toggle = $('.navbar-toggle');
                // Only click if toggle is visible and ...
                if (toggle && toggle.is(':visible') && \
                    // ... menu item is not a dropdown toggle.
                    !$(this).hasClass('dropdown-toggle')) {
                        toggle.trigger("click");
                }
            });
        });
    }});

Yes, and I also discovered later I need to use Ember.$ in order to deploy successfully.

@varblob: Not sure I completely understand the reason for using Ember.run() here, can you explain more?

Need to define var self = Ember.$(this) before the call to Ember.run() and use self inside the call.

@kigish

My real world understanding is that it has limited impact unless you’re doing a lot of view changes in the callback in a lot of views. Other people for whom this has fixed real problems feel free to jump in here.

My best practice understanding is based off of this note

Runs the passed target and method inside of a RunLoop, ensuring any deferred actions including bindings and views updates are flushed at the end.

Normally you should not need to invoke this method yourself. However if you are implementing raw event handlers when interfacing with other libraries or plugins, you should probably wrap all of your code inside this call.

Ember.run() makes your code testable. Testing disables autorun, and you may get errors when trying to test async code not wrapped in it. See this post I commented on: Setting up testing - #2 by joshpfosi

What is current way to add this code with view being deprecated? I tried on add it to a component of the Navbar. Alternative without using a component?

Good point, haven’t gotten that far yet to start worrying.

Maybe this addon can work in your case. ember-bootstrap-nav-link - npm

Tried this addon. There is a flickering issue with the “data-toggle” used in the menu link. The flickering will happen when on desktop version. I finally used the “visible-xs” to fix the flickering and just used attribute binding to allow data-attributes used on the link-to. Here is the code.

//app/initializers/data-attribute.js
import Ember from 'ember';

export function initialize(/* application */) {
  const attributeNames = ['data-auto-id', 'data-toggle', 'data-target'];

  Ember.TextField.reopen({
    attributeBindings: attributeNames
  });

  Ember.LinkComponent.reopen({
    attributeBindings: attributeNames
  });

  Ember.TextArea.reopen({
    attributeBindings: attributeNames
  });
}

export default {
  name: 'data-attribute',
  initialize
};

template:

<nav class="navbar navbar-default">
  <div class="container-fluid">
    <div class="navbar-header">
      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse" aria-expanded="false">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a href="/" class="navbar-brand"></a>
    </div>
    <div class="collapse navbar-collapse" id="hw-navbar">
      <ul class="nav navbar-nav navbar-right">
        <li class="font-kai">{{#link-to 'works' current-when='works work' class='hidden-xs'}}{{t "menu.works"}}{{/link-to}}</li>
        <li class="font-kai">{{#link-to 'works' current-when='works work' data-toggle='collapse' data-target='.navbar-collapse' class='visible-xs'}}{{t "menu.works"}}{{/link-to}}</li>
        <li class="font-kai">{{#link-to 'proses' current-when='proses prose' class='hidden-xs'}}{{t "menu.proses"}}{{/link-to}}</li>
        <li class="font-kai">{{#link-to 'proses' current-when='proses prose' data-toggle='collapse' data-target='.navbar-collapse' class='visible-xs'}}{{t "menu.proses"}}{{/link-to}}</li>
        <li class="font-kai">{{#link-to 'quotes' current-when='quotes quote' class='hidden-xs'}}{{t "menu.quotes"}}{{/link-to}}</li>
        <li class="font-kai">{{#link-to 'quotes' current-when='quotes quote' data-toggle='collapse' data-target='.navbar-collapse' class='visible-xs'}}{{t "menu.quotes"}}{{/link-to}}</li>
        <li class="font-kai">{{#link-to 'people' current-when='people person' class='hidden-xs'}}{{t "menu.people"}}{{/link-to}}</li>
        <li class="font-kai">{{#link-to 'people' current-when='people person' data-toggle='collapse' data-target='.navbar-collapse' class='visible-xs'}}{{t "menu.people"}}{{/link-to}}</li>
      </ul>
    </div>
  </div>
</nav>

I added some extra note avoiding flickering and updated the sample code. That flickering is not Ember related, but this note helps to use the collapse properly:

Note: Don’t forget the .in selector in data-target in the component line, because we would like to close the menu only when it is open. If you miss the .in, the menu in desktop mode would try to collapse which would cause a strange flickering.

This worked for me:

Template:

<div class="collapse navbar-collapse" onclick={{action 'navBarClicked' value='target'}}>

Controller or component:

/**
 * Collapses the navigation menu on mobile/tablet.
 */
navBarClicked(target) {
  // Do not collapse on desktop (i.e. if the button navbar toggle is hidden)
  if (Ember.$('button.navbar-toggle').is(':hidden')) {
    return;
  }
  // Do not collapse if target is a sub menu
  if (Ember.$(target).hasClass('dropdown-toggle')) {
    return;
  }

  Ember.$('.navbar-collapse').collapse('hide');
}

Hope it helps!