Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multilevel Menu using Knockout JS and Bootstrap

I am trying to make a multilevel menu using Knockout JS and Bootstrap, but I'm not sure how.

Here's what I have so far:

var Person = function (name, children) {
    this.name = ko.observable(name);
    this.children = ko.observableArray(children || []);
};

var PeopleModel = function () {
    this.people = ko.observableArray([
        new Person("Bob", [
            new Person("Jan"),
            new Person("Don", [
                new Person("Ted"),
                new Person("Ben", [
                    new Person("Joe", [
                        new Person("Ali"),
                        new Person("Ken")
                    ])
                ]),
                new Person("Doug")
            ])
        ]),
        new Person("Ann", [
            new Person("Eve"),
            new Person("Hal")
        ])
    ]);

    this.addChild = function (name, parentArray) {
        parentArray.push(new Person(name));
    };
};

$(function () {
    ko.applyBindings(new PeopleModel());
});
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<script src="//code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-debug.js"></script>

<div class="navbar navbar-default " role="navigation">
    <div class="navbar-header">
        <button type="button" class="navbar-toggle" 
                data-toggle="collapse" data-target=".navbar-collapse">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
        </button>
        <a class="navbar-brand" href="#">Project name</a>
    </div>
    <div class="navbar-collapse collapse">
        <ul id="people" class="nav navbar-nav"
            data-bind="template: { name: 'personTmpl', foreach: people }">
        </ul>
    </div>
</div>

<script id="personTmpl" type="text/html">
    <li data-bind="css: { dropdown: children().length > 0 }">
        <a href="#" data-bind="text: name, visible: children().length < 1"></a>

        <a href="#" class="dropdown-toggle"
           data-bind="text: name, visible: children().length > 0" 
           data-toggle="dropdown" role="button"
           aria-haspopup="true" aria-expanded="false">
            <span class="caret"></span>
        </a>

        <ul class="dropdown-menu" 
            data-bind="template: { name: 'personTmpl', foreach: children }">
        </ul>
    </li>
</script>
like image 829
baris usanmaz Avatar asked Sep 04 '26 07:09

baris usanmaz


2 Answers

Thank you @Roy J, I used the css link you posted.

Here's how I solved it:

var Person = function (name, children, rootItems) {
        this.name = ko.observable(name);
        this.children = ko.observableArray(children || []);
        this.rootItems = ko.observable(rootItems || false);
    };

    var PeopleModel = function () {
        this.people = ko.observableArray([
            new Person("Bob", [
                new Person("Jan"),
                new Person("Don", [
                    new Person("Ted"),
                    new Person("Ben", [
                        new Person("Joe", [
                            new Person("Ali"),
                            new Person("Ken")
                        ])
                    ]),
                    new Person("Doug")
                ])
            ], true),
            new Person("Ann", [
                new Person("Eve"),
                new Person("Hal")
            ], true)
        ]);

        this.addChild = function (name, parentArray) {
            parentArray.push(new Person(name));
        };
    };

$(function () {
    ko.applyBindings(new PeopleModel());
});
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<script src="//code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-debug.js"></script>

 <style type="text/css">
    .dropdown-submenu{position:relative;}
    .dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px;}
    .dropdown-submenu:hover>.dropdown-menu{display:block;}
    .dropdown-submenu>a:after{display:block;content:" ";float:right;width:0;height:0;border-color:transparent;border-style:solid;border-width:5px 0 5px 5px;border-left-color:#cccccc;margin-top:5px;margin-right:-10px;}
    .dropdown-submenu:hover>a:after{border-left-color:#ffffff;}
    .dropdown-submenu.pull-left{float:none;}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px;}
</style>

<div class="navbar navbar-default " role="navigation">
    <div class="navbar-header">
        <button type="button" class="navbar-toggle" 
                data-toggle="collapse" data-target=".navbar-collapse">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
        </button>
        <a class="navbar-brand" href="#">Project name</a>
    </div>
    <div class="navbar-collapse collapse">
        <ul id="people" class="nav navbar-nav"
            data-bind="template: { name: 'personTmpl', foreach: people }">
        </ul>
    </div>
</div>

<script id="personTmpl" type="text/html">
    <li data-bind="visible: rootItems(), css: { dropdown: children().length > 0 }">
        <a href="#" data-bind="text: name, visible: children().length < 1"></a>

        <a href="#" class="dropdown-toggle" data-bind="visible: children().length > 0" 
            data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
            <span data-bind="text: name"></span>
            <span class="caret"></span>
        </a>

        <ul class="dropdown-menu" data-bind="template: { name: 'personTmpl', foreach: children }"></ul>
    </li>

    <li data-bind="visible: rootItems() ? false : true, css: { 'dropdown-submenu': children().length > 0 }">
        <a href="#" data-bind="text: name, visible: children().length < 1"></a>

        <a href="#" class="dropdown-toggle" data-bind="visible: children().length > 0" 
            data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
            <span data-bind="text: name"></span>
        </a>

        <ul class="dropdown-menu" data-bind="template: { name: 'personTmpl', foreach: children }"></ul>
    </li> 
</script>
like image 63
baris usanmaz Avatar answered Sep 06 '26 21:09

baris usanmaz


Partial answer: You have a text binding on the anchor that is supposed to have a caret inside it. The text binding will overwrite all contents, which is why you get no caret. Instead:

    <a href="#" class="dropdown-toggle" data-bind="visible: children().length > 0" 
       data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
        <span data-bind="text: name"></span>
        <span class="caret"></span>
    </a>

My fiddle.

I still don't get submenus, but the caret does indicate that the HTML knows about Don's children. This example might help.

Officially, it looks like submenus are unsupported in Bootstrap 3.

like image 36
Roy J Avatar answered Sep 06 '26 20:09

Roy J



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!