So let's say I've got this view and "Menu" is a collection of menu objects which may also have a collection of menu objects named Children as a property.
Here's the view -
<div class="collapse navbar-collapse navbar-right navbar-main-collapse">
<ul class="nav navbar-nav">
<li repeat.for="m of menu" class.bind="m.type=='child' ? 'dropdown' : ''" as-element="compose" data-type="${m.type}"
view="/client/landing/menubar/${m.type}menu"></li>
</ul>
</div>
And there are two views I'm using as my recursive menu, like so -
(View - no child collection)
<template>
<a href.bind="menu.link" data-info="${menu.type}">${menu.displayText}</a>
</template>
(View - with child collection)
<template>
${menu.displayText} <b class="caret"></b>
<ul class="dropdown-menu">
<li repeat.for="m of menu.children" data-info="${m.type}" class.bind="m.haschildren ? 'dropdown' : ''"
as-element="compose" view="/client/landing/menubar/${m.type}menu"></li>
</ul>
</template>
And the viewmodels for these two views both look like this -
import { customElement, bindable, inject } from "aurelia-framework";
#customElement("parentmenu")
export class ParentMenu {
#bindable menu;
}
I'm getting something. The views do display (although without recursion). The problem is that the repeat.for="m of menu" simply doesn't work below the bind in the first view.
Where have I gone wrong?
There's no need to use compose. Your menu could be a lot simpler, like:
<template bindable="model">
<ul>
<li repeat.for="item of model">
<a href.bind="item.link">${item.displayText}<a/>
<menu if.bind="item.children && item.children.length" model.bind="item.children"></menu>
</ul>
</template>
Usage:
<menu model.bind="menu"></menu>
JS:
menu = [
{
displayText: '1',
href: ''
},
{
displayText: '2',
children: [
{
displayText: '3',
href: ''
},
{
displayText: '4',
href: '',
children: [
{
displayText: '3',
href: ''
}
]
}
]
}
];
Related
data: {
menuItems: [{
name: 'Item 1',
children: [{
name: 'Subitem 1'
}, {
name: 'Subitem 2'
}, {
name: 'Subitem 3'
}]
},
{
name: 'Item 2'
}
],
selectedDropdown: 'None'
},
methods: {
setSelectedItem(item) {
this.selectedDropdown = item;
}
},
ready: function() {
$('.dropdown-submenu a.test').on("click", function(e) {
$(this).next('ul').toggle();
e.stopPropagation();
e.preventDefault();
});
}
.dropdown-submenu {
position: relative;
}
.dropdown-submenu .dropdown-menu {
top: 0;
left: 100%;
margin-top: -1px;
}
<ul>
<li :class="{ current : item === value }" v-for="item in list" #click="select(item)">{{ item }}</li>
</ul>
<ul class="dropdown-menu" v-if="item.children">
<li v-for="child in item.children"><a tabindex="-1" href="#" #click="setSelectedItem(child.name)">{{child.name}}</a></li>
CodePen link https://codepen.io/santoshch/pen/mdWwepg
Tried adding sub menu in the dropdown, But unable to do that, i mean multi level dropdown. Taken one more ul and li tag to display items, But not sure how to proceed.
In the data i have already passed items and value, But for sub multi level i need to add few items.
For the above codepen, Tried to add code for sub multi level items, Then i am getting error
You could extend your array list (from your codepen example) with objects:
list: [
'Orange',
'Apple',
'Kiwi',
'Lemon',
'Pineapple',
{
name: 'Others',
children: [
'Strawberries',
'Raspberries',
'Blueberries'
]
},
'Last Fruit'
]
And when you loop over the list you check for these objects and output them in a nested <ul> element:
<ul>
<template v-for="(item, index) in list">
<li v-if="item.name" :key="index" :class="{ current : item === value }">
{{ item.name }}
<ul>
<li v-for="(childItem, childIndex) in item.children" :class="{ current : childItem === value }" :key="childIndex" #click="select(childItem)">
{{ childItem }}
</li>
</ul>
</li>
<li v-else :key="index" :class="{ current : item === value }" #click="select(item)">{{ item }}</li>
</template>
</ul>
Here is a working example (without styling the submenu)
https://jsbin.com/caroguwori/edit?html,js,output
I am working on a project with Mean JS and I am using the template offered by them at https://github.com/meanjs/mean .
Basically I want to do:
if (more than 1 sub-menus)
make it drop-down
else
make just a button and if that button clicked take it to state of the sub menu.
A button on the navigation bar will be defined as drop-down. It will have 2 sub-menus. This main button will be visible to all types of user. However, only 1 sub-menu on the drop down will be visible to general users. All 2 sub-menus will be visible to the admin users. I want to define it as drop down, but if there is only 1 sub-menu, don't show it as drop-down. When a user clicks on the main button it will take it to the page defined in the sub-menu.
Here is the code that is in the header.client.view.html (This is from the template, I have not change it.)
<nav class="collapse navbar-collapse" collapse="!isCollapsed" role="navigation">
<ul class="nav navbar-nav" ng-if="menu.shouldRender(authentication.user);">
<li ng-repeat="item in menu.items | orderBy: 'position'" ng-if="item.shouldRender(authentication.user);" ng-switch="item.type" ng-class="{ active: $state.includes(item.state), dropdown: item.type === 'dropdown' }" class="{{item.class}}" dropdown="item.type === 'dropdown'">
<a ng-switch-when="dropdown" class="dropdown-toggle" dropdown-toggle role="button">{{::item.title}} <span class="caret"></span></a>
<ul ng-switch-when="dropdown" class="dropdown-menu">
<li ng-repeat="subitem in item.items | orderBy: 'position'" ng-if="subitem.shouldRender(authentication.user);" ui-sref-active="active">
<a ui-sref="{{subitem.state}}" ng-bind="subitem.title"></a>
</li>
</ul>
<a ng-switch-default ui-sref="{{item.state}}" ng-bind="item.title"></a>
</li>
</ul>
</nav>
Here is what goes in the articles.client.config.js
// Configuring the Articles module
angular.module('articles').run(['Menus',
function(Menus) {
// Add the articles dropdown item
Menus.addMenuItem('topbar', {
title: 'Blog',
state: 'articles',
type: 'dropdown',
roles: ['*'],
position: 1
});
// Add the dropdown list item
Menus.addSubMenuItem('topbar', 'articles', {
title: 'Visit Blog',
state: 'articles.list',
roles: ['user', 'admin']
});
// Add the dropdown create item
Menus.addSubMenuItem('topbar', 'articles', {
title: 'Create Blog Post',
state: 'articles.create',
roles: ['admin']
});
}
]);
I have a nested (tertiary) menu list with a jquery click event on the back. jQuery click does not fire when the menu item is clicked. The jQuery event works well if the values inside the HTML are static.
HTML:
<div>
<ul class="collapsible-list" ng-controller="ViewCtrl">
<li class="collapsible-list-subnav" ng-repeat="view in views">
<a class="collapsible-list-parent">{{view.name}}</a>
<ul class="collapsible-list secondary">
<li class="collapsible-list-subnav">
<a class="collapsible-list-parent">Public Views</a>
<ul class="collapsible-list tertiary">
<li ng-repeat="publicview in view.publicviews">
<a>{{publicview.title}}</a>
</li>
</ul>
</li>
<li class="collapsible-list-subnav">
<a class="collapsible-list-parent">Private Views</a>
<ul class="collapsible-list tertiary">
<li ng-repeat="privateview in view.privateviews">
<a>{{privateview.title}}</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
Javascript:
define([ 'angular', 'controllers-module'], function(angular,
controllers) {
controllers.controller("ViewCtrl", [
"$scope",
"$rootScope",
"directiveBinder",
'$timeout',
'$stateParams',
'$resource',
'$state',
function($scope, $rootScope,directiveBinder, $timeout, $stateParams, $resource, $state) {
$scope.engines = [ {
name : 'name1',
publicviews : [ {
title : 'First public View'
} ],
privateviews : [ {
title : 'First private View'
} ]
}, {
name : 'name2',
publicviews : [ {
title : 'Second public View'
} ],
privateviews : [ {
title : 'Second private View'
} ]
} ];
$('.collapsible-list-parent').click(function(e) {
e.preventDefault();
$(this).next().slideToggle('fast');
if ($(this).parent().hasClass('open')) {
$(this).parent().removeClass('open');
} else {
$(this).parent().addClass('open');
}
});
});
Because the elements are added dynamically by ng-repeat the .click event is not binded to them. Try to use .delegate
$( "ul" ).delegate( ".collapsible-list-parent", "click", function() {
// code here
});
When we use ng-repeat and need to trigger a jquery click event just try this it worked for me.
$(document).on("click", ".className", function() {
//your code here...
});
I don't think using jQuery code in an angularjs controller is the right way to do this, a sample to do the same without the animation will be like
var app = angular.module('my-app', [], function() {
})
app.controller('ViewCtrl', function($scope) {
$scope.views = [{
name: 'name1',
publicviews: [{
title: 'First public View'
}],
privateviews: [{
title: 'First private View'
}]
}, {
name: 'name2',
publicviews: [{
title: 'Second public View'
}],
privateviews: [{
title: 'Second private View'
}]
}];
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="my-app">
<ul class="collapsible-list" ng-controller="ViewCtrl">
<li class="collapsible-list-subnav" ng-repeat="view in views">
<a class="collapsible-list-parent" ng-click="open = !open">{{view.name}}</a>
<ul class="collapsible-list secondary" ng-show="open">
<li class="collapsible-list-subnav">
<a class="collapsible-list-parent" ng-click="popen = !popen">Public Views</a>
<ul class="collapsible-list tertiary" ng-show="popen">
<li ng-repeat="publicview in view.publicviews">
<a>{{publicview.title}}</a>
</li>
</ul>
</li>
<li class="collapsible-list-subnav">
<a class="collapsible-list-parent" ng-click="ropen = !ropen">Private Views</a>
<ul class="collapsible-list tertiary" ng-show="ropen">
<li ng-repeat="privateview in view.privateviews">
<a>{{privateview.title}}</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
If you want to use animations you can make use of angularjs animation which uses css3 animations.
I have a bootstrap 3 menu which I am trying to use Angular JS on.
I have the basic menu working, and the ng-class correctly works, applying it only in the menu has children.
What I am trying to do now is to only have the menu "work" (display the second nested UL) only if the parent LI has child data. So, I have this:
<ul class="nav navbar-nav">
<li ng-class="{'dropdown':(n.children.length)}" ng-repeat="n in navData">
<a data-target="#" ng-attr="{'data-toggle=dropdown':(n.children.length>0)}">{{n.label}}</a>
<ul class="dropdown-menu" role="menu">
<li ng-repeat="p in n.children">{{p.label}}</li>
</ul>
</li>
</ul>
The part I have wrong, or that does not work is the ng-attr on the A tag. The data-toggle=dropdown is what causes the menu to work. Currently, none of the menus work even if they have children.
My model is
var nav = [{
label: 'Pages',
value: 'pages',
children: [{
label: 'Home',
value: 'home'
}, {
label: 'Left Nav',
value: 'left-nav'
}]
}, {
label: 'Components',
value: 'components'
}];
So, "Pages" has children and "Components" does not. The ng-class works as expected.
EDIT: I have added a "toggle" value to the model, set to "dropdown" or "" and then this works:
<ul class="nav navbar-nav">
<li ng-class="{'dropdown':(n.children.length)}" ng-repeat="n in navData">
<a data-target="#" data-toggle="{{n.toggle}}">{{n.label}}</a>
<ul class="dropdown-menu" role="menu">
<li ng-repeat="p in n.children">{{p.label}}</li>
</ul>
</li>
</ul>
I have added a "toggle" value to the model, set to "dropdown" or "" and then this works:
<ul class="nav navbar-nav">
<li ng-class="{'dropdown':(n.children.length)}" ng-repeat="n in navData">
<a data-target="#" data-toggle="{{n.toggle}}">{{n.label}}</a>
<ul class="dropdown-menu" role="menu">
<li ng-repeat="p in n.children">{{p.label}}</li>
</ul>
</li>
</ul>
I have Angular 1.2.1 and Bootstrap 3.0.2
Plain vanilla nav menu with a drop down works fine but when I try to generate my menu using ng-repeat the submenu will not fire.
The HTML:
<ul class="nav navbar-nav" ng-repeat="data in main_menu">
<li ng-class="{'dropdown' : data.nodes}">
<a href="{{data.link}}" ng-class="{'dropdown-toggle' : data.nodes}">{{data.name}} <b class="caret" ng-if="data.nodes"></b>
<ul ng-if="data.nodes" ng-repeat="items in data.nodes" class="dropdown-menu">
<li>{{items.name}}</li>
</ul>
</li>
</ul>
In the Controller:
$scope.main_menu = [
{
name: 'Home',
class: '',
link: '/',
nodes: false
},
{
name: "DropDown",
class: 'dropdown-toggle',
link: '#',
nodes: [
{
name: "Node2",
class: '',
link: 'link'
},
{
name: "Node2",
class: '',
link: 'link'
},
{
name: "Node2",
class: '',
link: 'link'
},
{
name: "Node2",
class: '',
link: 'link'
}
]
}
];
The normal bootstrap html submenu function works fine ..
Any suggestion ?
After checking ..
The Angular html Block is not correct, with this adjustment it renders the correct html - the drop down still does not work
<ul class="nav navbar-nav" >
<li ng-repeat="data in main_menu" ng-class="{'dropdown' : data.nodes}">
{{data.name}} <b class="caret" ng-if="data.nodes"></b>
<ul ng-if="data.nodes" class="dropdown-menu">
<li ng-repeat="items in data.nodes">{{items.name}}</li>
</ul>
</li>
</ul>
Two steps:
Add attr data-toggle="dropdown" for link.
Update controller, change link: '#' to link: ''.