Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bootstrap dropdowns in Durandal 2.0

I am trying to wire up some bootstrap dropdown navigation in a durandal 2.0 app:

<ul class="nav nav-pills">
    <li class="dropdown">
        <a class="dropdown-toggle" data-toggle="dropdown" href="#">
            <span>Dropdown</span>
            <b class="caret"></b>
        </a>
        <ul class="dropdown-menu">
            <li><a tabindex="-1" href="#/view/a>Option A</a></li>
            <li><a tabindex="-1" href="#/view/b">Option B</a></li>
        </ul>
    </li>
</ul>

Clicking "dropdown" causes Durandal's router to attempt to navigate to the root route (which is "#" or ""). And never opens the menu. Is this correct? Workarounds?

Update

Tabs suffer the very same problem:

<div class="tabbable">
    <ul class="nav nav-tabs">
        <li class="active"><a href="#tab1" data-toggle="tab">Section 1</a></li>
        <li><a href="#tab2" data-toggle="tab">Section 2</a></li>
    </ul>
    <div class="tab-content">
        <div class="tab-pane active" id="tab1">
        <p>I'm in Section 1.</p>
    </div>
    <div class="tab-pane" id="tab2">
        <p>Howdy, I'm in Section 2.</p>
        </div>
    </div>
</div>
like image 797
Matthew James Davis Avatar asked Feb 15 '23 19:02

Matthew James Davis


2 Answers

The problem was actually quite simple. There was no conflict with the routing module. I assumed the bootstrap javascript was included by default on the starter kit, but it is not.

The fix is as simple as adding an additional line to my require dependencies:

var system = require('durandal/system'),
    app = require('durandal/app'),
    viewLocator = require('durandal/viewLocator');
require('bootstrap');

Note that my require.config is:

require.config({
    paths: {
        'text': '../lib/require/text',
        'durandal':'../lib/durandal/js',
        'plugins' : '../lib/durandal/js/plugins',
        'transitions' : '../lib/durandal/js/transitions',
        'knockout': '../lib/knockout/knockout-2.3.0',
        'bootstrap': '../lib/bootstrap/js/bootstrap',
        'jquery': '../lib/jquery/jquery-1.9.1'
    },
    shim: {
        'bootstrap': {
            deps: ['jquery'],
            exports: 'jQuery'
       }
    }
});
like image 168
Matthew James Davis Avatar answered Feb 27 '23 11:02

Matthew James Davis


Probably the best option in the long run would be converting those bootstrap widgets into knockout custom bindings (like e.g. https://github.com/billpull/knockout-bootstrap) or even better into native Durandal widgets.

Check out how Durandal 1.2 messageBox is implemented. This can be used as a rough guideline to convert bootstrap tabs and dropdowns into Durandal widgets. As you can see href attributes in the view have been replaced by data-bind="click: ...". In addition you'd have to convert the Bootstrap JavaScript into a Durandal viewmodel.

e.g. http://twitter.github.io/bootstrap/javascript.html#modals

<div class="modal hide fade">
  <div class="modal-header">
    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
    <h3>Modal header</h3>
  </div>
  <div class="modal-body">
    <p>One fine body…</p>
  </div>
  <div class="modal-footer">
    <a href="#" class="btn">Close</a>
    <a href="#" class="btn btn-primary">Save changes</a>
  </div>
</div>

becomes
https://github.com/dFiddle/dFiddle-1.2/blob/gh-pages/App/durandal/messageBox.html

<div class="messageBox">
    <div class="modal-header">
        <h3 data-bind="html: title"></h3>
    </div>
    <div class="modal-body">
        <p class="message" data-bind="html: message"></p>
    </div>
    <div class="modal-footer" data-bind="foreach: options">
        <button class="btn" data-bind="click: function () { $parent.selectOption($data); }, html: $data, css: { 'btn-primary': $index() == 0, autofocus: $index() == 0 }"></button>
    </div>
</div>

Update: Here's a minimalistic tab widget implementation. Live version at http://dfiddle.github.io/dFiddle-2.0/#bootstrap

view:

<div class="tabs">
    <ul class="nav nav-tabs" data-bind="foreach: { data: settings.items }">
        <li data-bind="css: {active: isActive}">
            <a data-bind="text: name, click: $parent.toggle.bind($parent)"></a>
        </li>
    </ul>

    <div class="tab-content" data-bind="foreach: { data: settings.items}">
        <div class="tab-pane"  data-bind="html: content, css: {active: isActive}"></div>
    </div>
</div>

viewmodel:

define(['durandal/composition', 'jquery'], function(composition, $) {

    var ctor = function() { };

    ctor.prototype.activate = function(settings) {
        this.settings = settings;
    };

    ctor.prototype.detached = function() {
        console.log('bootstrap/widget/viewmodel: detached', arguments, this);
    };

    ctor.prototype.toggle = function(model, event){
        this.deactivateAll();
        model.isActive(true);

    };

    ctor.prototype.deactivateAll = function(){

        $.each(this.settings.items(), function(idx, tab){
            tab.isActive(false);
        });
    };

    return ctor;
});
like image 37
RainerAtSpirit Avatar answered Feb 27 '23 11:02

RainerAtSpirit