3

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>
KyleMit
  • 30,350
  • 66
  • 462
  • 664
baris usanmaz
  • 839
  • 1
  • 13
  • 31
  • 2
    Could you put this in a fiddle and explain exactly what part you are having a problem with? – swornabsent Sep 09 '15 at 13:45
  • 1
    Inspecting items in Chrome debugger shows that the structure is in place. There's apparently something about the structure that Bootstrap doesn't like. – Roy J Sep 09 '15 at 14:09
  • When I try on my computer I see the root values in array (Bob and Ann). Also I see the descendant values (Jan-Don and Eve-Hal). But I can not see other values under Don. You can see the fiddle but fiddle does not work properlly. http://jsfiddle.net/d3ks0dgh/7/ – baris usanmaz Sep 09 '15 at 14:29
  • 1
    I found a fiddle that appears to accomplish what you want: http://jsfiddle.net/MCNK8/3/ It may require you changing your structure slightly. – Daniel Brown Sep 09 '15 at 14:46

2 Answers2

4

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>
Community
  • 1
  • 1
baris usanmaz
  • 839
  • 1
  • 13
  • 31
1

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.

Community
  • 1
  • 1
Roy J
  • 42,522
  • 10
  • 78
  • 102