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: ''.
Related
I have a Typescript project and had trouble trying to display divider. I posted a question on SO here and received an answer which I believe will work.
However when I tried to implement a ternary on the class I get the error Warning Missing attribute name on both options.
Here is my list item:
<li repeat.for="menu of route.settings.nav" class=${menu.navSettings.divider ? 'divider' : 'dropdown-submenu'}>
I am getting the warning on both the "divider" option and the "dropdown-submenu" option. Further with this modification it now wont render the next level of menu items.
Why am I getting a missing attribute warning and how can I make this work..
Background context
Here is the whole navmenu:
<ul if.bind="route.settings.nav" class="dropdown-menu">
<li repeat.for="menu of route.settings.nav" class=${menu.navSettings.divider ? 'divider' : 'dropdown-submenu'}>
<a href.bind="menu.href" if.bind="!menu.navSettings.subNav"><span class="glyphicon glyphicon-${ menu.navSettings.icon }"></span> ${menu.title}</a>
<a href.bind="menu.href" if.bind="menu.navSettings.subNav" class="dropdown-toggle" data-toggle="dropdown">
<span class="glyphicon glyphicon-${ menu.navSettings.icon }"></span> ${menu.title} <span class="caret-right"></span>
</a>
<ul if.bind="menu.navSettings.subNav" class="dropdown-menu">
<li repeat.for="subMenu of menu.navSettings.subNav" class="dropdown-submenu">
<a href.bind="subMenu.href" if.bind="!subMenu.subNavSettings.subSubNav"><span class="glyphicon glyphicon-${ subMenu.subNavSettings.icon }"></span> ${subMenu.title}</a>
<a href.bind="subMenu.href" if.bind="subMenu.subNavSettings.subSubNav" class="dropdown-toggle" data-toggle="dropdown">
<span class="glyphicon glyphicon-${ subMenu.subNavSettings.icon }"></span> ${subMenu.title} <span class="caret-right"></span>
</a>
<ul if.bind="subMenu.subNavSettings.subSubNav" class="dropdown-menu">
<li repeat.for="lowestSubMenu of subMenu.subNavSettings.subSubNav" class="dropdown-submenu">
<a href.bind="lowestSubMenu.href"> <span class="glyphicon glyphicon-${ lowestSubMenu.subSubNavSettings.icon }"></span> ${lowestSubMenu.title}</a>
</li>
</ul>
</li>
</ul>
<!--<span class="divider" if.bind="menu.navSettings.divider"> </span>-->
</li>
</ul>
Finally here is the route containing the first level dropdown options including the divider:
{
route: "clients",
name: "clients",
moduleId: PLATFORM.moduleName("../components/clients/clientList/clientList"),
title: "Clients",
nav: true,
settings: {
nav: [
{
href: "#clients/clientsList",
title: "Client List",
navSettings: {
icon: "list",
roles: ["Employee", "Admin"],
}
},
{
navSettings: {
roles: ["Employee", "Admin"],
divider: true,
}
},
{
href: "#clients/Create",
title: "Create Client",
navSettings: {
icon: "user",
roles: ["Employee", "Admin"],
}
}
],
icon: "user",
auth: true,
roles: ["Employee", "Admin"],
pos: "left"
}
},
You forgot to add quotes around your class-attribute value:
<li repeat.for="menu of route.settings.nav"
class="${menu.navSettings.divider ? 'divider' : 'dropdown-submenu'}">
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: ''
}
]
}
]
}
];
I have this sidebar.html:
<ul id="menu-sidebar">
<li class="has_sub" ng-repeat="menu in menus | filter: filterTier | filter: filterRole">
<a ng-if="menu.url != '#'" ui-sref="{{ menu.url }}" class="waves-effect" ui-sref-active="active subdrop">
<i class="fa fa-{{ menu.icon }}"></i> <span> {{ menu.name }}</span>
</a>
<a ng-if="menu.url == '#'" class="waves-effect" ui-sref-active="active subdrop" href="#">
<i class="fa fa-{{ menu.icon }}"></i> <span> {{ menu.name }}</span>
</a>
<ul class="list-unstyled" ng-if="menu.sub_menus.length > 0">
<li ng-repeat="submenu in menu.sub_menus">
<a ui-sref="{{ submenu.url }}" ui-sref-active="active">{{ submenu.name }}</a>
</li>
</ul>
</li>
</ul>
And my appController.js:
$scope.menus = [{name: 'Customers', url: 'customers', icon: 'users', tier: 1, role: 1},
{name: 'Settings', url: '#', icon: 'gear', tier: 1, role: 1,
sub_menus: [{name: 'Admin', url: 'settings.admin'}, {name: 'Outlet', url: 'settings.outlet'} ]
}];
Those code above will produce sidebar menu.
Yes above code is worked perfectly.
I've got 2 questions:
As above I need to repeat my <a> tag and hide it manually if it '#' because it always return me an error if I put # or blank inside ui-sref = {{menu.url}}
How can I create my sidemenu only once?. Right now my controller will load the $scope.menus every time the page is loaded (I define it as appController and put it on my body tag so it always return me those menu).
Thanks in advance!
As a tag and ui-router are used for going to a link so of course they will give you error if you don't give address to them.
Try this :
$( document ).ready(function() {
//your code
})/
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>