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>
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>
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With