How to get menus and submenus data from an array? - javascript

I am creating a webpage using smart admin.I am getting left menu datas from an array.I need display menus and sub menus properly based on parent Id of each data using angular. But I don't know how to do it.Can anyone help me?please.
Script:
var app = angular.module('myApp', []);
app.controller('myController', ['$scope', '$http', function ($scope, $http) {
$scope.Menus = [];
$http.get('/list/GetSiteMenu').then(function (data) {
$scope.Menus = data.data.data.record;
}, function (error) {
alert('Error');
});
}]);
Html:
<nav ng-repeat="menuData in Menus">
<ul>
<li>
<ul>
<li><a></a></li>
</ul>
</li>
</ul>
</nav>
console.log($scope.Menus) will be in this format:

Need to iterate over menuData.menu_roles
<nav ng-repeat="menuData in Menus">
<ul>
<li>
<ul>
<li ng-repeat ="subMenu in menuData.menu_roles">
<a></a>
</li>
</ul>
</li>
</ul>
</nav>

You can use something like this according to your json
Use rootscope instead of scope to store Menus
<section class="sidebar">
<!-- sidebar menu: : style can be found in sidebar.less -->
<ul class="sidebar-menu">
<li class="{{menuItem.LiCssClass}}" ng-repeat="menuItem in $root.menuList" ng-class="{active:isActive('{{menuItem.NavigationURL}}')}">
<a ng-href="{{menuItem.NavigationURL}}" ng-click="sidebar()">
<i class="{{menuItem.ICssClass}}"></i>
<span class="{{menuItem.SpanCssClass}}"> {{menuItem.DisplayName}}</span>
<i class="{{menuItem.TreeViewIcon}}"></i>
</a>
<ul class="{{menuItem.UiCssClass}}">
<li id="{{subMenuItem.TagName}}" ng-repeat="subMenuItem in menuItem.SubMenu" ng-class="{active:isActive('{{subMenuItem.NavigationURL}}')}">
<a ng-href="{{subMenuItem.NavigationURL}}">
<i class="{{subMenuItem.ICssClass}}"></i><span>{{subMenuItem.DisplayName}}</span>
</a>
</li>
</ul>
</li>
</ul>
</section>

You can use ng-bootstrap-submenu
https://www.npmjs.com/package/ng-bootstrap-submenu
It's a module for add submenus items to parent menu items and it's easy to use

Related

How to know on which page I am now

I have a list of tabs, I want to know my current location for every time I click specific tab, I want my MainCTRL which is the father of all tab controllers to know which tab is active now.
I have tried to use ng-click event and using service such $location.path() to get current path. But first I get the previous path and not the current path.
I added the code below for demonstration and also a codepen:
http://codepen.io/Barak/pen/wGPpoZ
Why I need such feaure, because I want some common buttons of the header to be disable for specific group of the tabs and active for the other part of the tabs.
var app = angular.module('app',[]);
app.controller("MainCTRL",["$scope","$location", function($scope,$location){
$scope.title = "Hello World";
$scope.onWhichPageIAM = function(){
console.log("He click me");
console.log("location:",$location.path());
}
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<nav id="tabs_navbar" class="navbar-collapse collapse" ng-app="app" ng-controller="MainCTRL">
<ul class="tabs">
<li class="tabs-item">
Analytics Summary
</li>
<li class="tabs-item">
TDCG
</li>
<li class="tabs-item">
Key Gas
</li>
<li class="tabs-item">
Duval Triangle
</li>
<li class="tabs-item">
Duval Pentagon
</li>
<li class="tabs-item">
NEI
</li>
<li class="tabs-item">
PTX
</li>
<li class="tabs-item">
Gas Trends
</li>
<li class="tabs-item">
Data Table
</li>
<li class="tabs-item">
Playground
</li>
<li class="tabs-item">
Analytics Settings
</li>
</ul>
</nav>
There's no need for a ng-click, you can use the $routeChangeSuccess (#Daniel Beck suggestion) signal broadcasted on the $rootScope, use it like this:
app.controller("MainCTRL",['$rootScope', "$scope","$location", function($rootScope, $scope,$location){
$rootScope.$on('$routeChangeSuccess', function(ev, current, previous) {
console.log("State change success");
console.log("location:", current); // info referent to the current state.
});
}]);
You can use the $state variable to get the active tab.
<div class="btn-group">
<button class="btn btn-primary btn-sm" uib-btn-radio="'mytabs'" ui-sref-active="active" ng-model="ActiveChild" ui-sref="tab1">FirstTab</button>
<button class="btn btn-primary btn-sm" uib-btn-radio="'mytabs'" ui-sref-active="active" ng-model="ActiveChild" ui-sref="tab2">SecondTab</button>
</div>
And use $scope.ActiveChild = $state.current.name; in your controller.
$state.current.name; should give you name of the active tab.

Run script after foreach list

I am using an external javascript library called Jarvismenu for my knockout app menu and in order to make the menu work (collapse/expand parent menu items when clicking on them) a script needs to be executed after the menu has loaded.
The menu looks like this:
<nav>
<ul data-bind="foreach: reports">
<li>
<span class="menu-item-parent" data-bind="text:title"></span>
<ul data-bind="foreach: reportItems">
<li>
</li>
</ul>
</li>
</ul>
</nav>
How can I achieve this in knockout 3.3.0?
There is additional data you can send into your foreach binding. In your case you would be interested in the afterRender option.
Your code would look like this
<nav>
<ul data-bind="foreach: { data: reports, afterRender: functionToCallWhenReportsRendered}">
<li>
<span class="menu-item-parent" data-bind="text:title"></span>
<ul data-bind="foreach: { data: reportItems, afterRender: functionToCallWhenItemsRendered}">
<li>
</li>
</ul>
</li>
</ul>
</nav>
Here is a link to more documentation:
http://knockoutjs.com/documentation/foreach-binding.html
segFault's answer helped but afterRender apparantly runs every time an item is added to the ul. What I had to do was in the afterRender function check if the last item was added like this:
functionToCallWhenReportsRendered: function (elements, data) {
if ($('#unorderedListId').children().length === this.myItems().length) {
// Execute handler
}
}
<nav>
<ul id="unorderedListId" data-bind="foreach: { data: reports, afterRender: functionToCallWhenReportsRendered}">
<li>
<span class="menu-item-parent" data-bind="text:title"></span>
<ul data-bind="foreach: { data: reportItems, afterRender: functionToCallWhenItemsRendered}">
<li>
</li>
</ul>
</li>
</ul>
</nav>

How to hide Show section with menu click?

I create a menu :
<ul class="navigation-bar navigation-bar-left">
<li class="active">About</li>
<li>schedule</li>
<li>speakers</li>
<li>Map</li>
<li class="featured"><i class="fa fa-play-circle fa-1x"></i>Register</li>
</ul>
And I want to show/hide the corresponding sections :
<section id="about" class="section dark">
Thanks !
Use jquery toggle() method to do this in simple way.
Refer http://www.w3schools.com/jquery/tryit.asp?filename=tryjquery_toggle
If you have a structure like
<ul class="navigation-bar navigation-bar-left">
<li class="active">About</li>
<li>schedule</li>
<li>speakers</li>
<li>Map</li>
<li class="featured"><i class="fa fa-play-circle fa-1x"></i>Register</li>
</ul>
<section id="sec-about" class="section dark"><h2>About</h2></section>
you could toggle the section like
document.getElementById('nav_about').addEventListener('click', function(){
var secAbout = document.getElementById('sec-about');
if( getComputedStyle(secAbout,null).display=='none') secAbout.style.display='block';
else secAbout.style.display='none';
}, false);
Compare jsfiddle

On hover closing specific list element

<ul class="open-close1" id="reportType">
<li class="active">
<div id="subcats">
<ul>
<li>
<img class="loading-spinner" src="store/$vendorSettingsDTO.vendorId/assets/themes/$vendorSettingsDTO.skinname/images/loading.gif" height="18"/>
</li>
</ul>
</div>
</li>
</ul>
<script>
jQuery(document).ready(function($){
jQuery.getJSON('subcat.ajx?vid=$vendorSettingsDTO.vendorId&cid=$catid', function(data) {
//Render subcatergories using Mustache.js (https://github.com/janl/mustache.js)
var subcat_mustache =
'<ul>\
<a style="font-size:14px; font-weight:bold; color:#0073B3;" class="opener"href="{{URL}}">{{description}}</a>\
{{#childs}}\
{{> child}}\
{{/childs}}\
</ul>';
var partials = {
child:
'<li>\
<a class="opener" href="{{URL}}">{{description}}</a>\
<div class="subnav1 hidden">\
<ul>\
{{#childs}}\
{{> child}}\
{{/childs}}\
</ul>\
</div>\
</li>'
}
jQuery("#subcats").html(Mustache.render(subcat_mustache, data, partials));
});
});
</script>
<script>
$(document).ready(function() {
$(".open-close1").hover(function (){
//$(this).children('.subnav1').toggle();
//console.log("$(this).children('.subnav1') "+$(this).children('.subnav1'));
//$(".subnav1", this).toggle();
$(this).find('.subnav1').toggle(); // p00f
});
});
</script>
This is the code I have in case of my category page. I implemented a logic to toggle the block on hover. But entire list of categories is toggling instead of specific list on which it is hovered.
Please help me its IRRITATING me. Thanks a lot for your help in advance.
I assume your rendered html like below;
<ul class="open-close1" id="reportType">
<li class="active">
<div id="subcats">
<ul>
<li>
<a class="opener" href="#">Item1</a>
<div class="subnav1 hidden">
<ul>
<li>Item1-1</li>
<li>Item1-2</li>
<li>Item1-3</li>
</ul>
</div>
</li>
<li>
<a class="opener" href="#">Item2</a>
<div class="subnav1 hidden">
<ul>
<li>Item2-1</li>
<li>Item2-2</li>
<li>Item2-3</li>
</ul>
</div>
</li>
<li>
<a class="opener" href="#">Item3</a>
<div class="subnav1 hidden">
<ul>
<li>Item3-1</li>
<li>Item3-2</li>
<li>Item3-3</li>
</ul>
</div>
</li>
</ul>
</div>
</div>
</li>
</ul>
You can use following js for above html structure;
$(".opener").hover(function (){
$(this).next().toggle();
});
Here is a working demo: http://jsfiddle.net/cubuzoa/EKu2q/
Edit: On your site, category section you mentioned loaded with ajax. So you need to run function after ajax load like below;
<script>
jQuery(document).ready(function($){
jQuery.getJSON('subcat.ajx?vid=$vendorSettingsDTO.vendorId&cid=$catid', function(data) {
//Render subcatergories using Mustache.js (https://github.com/janl/mustache.js)
var subcat_mustache =
'<ul>\
<a style="font-size:14px; font-weight:bold; color:#0073B3;" class="opener"href="{{URL}}">{{description}}</a>\
{{#childs}}\
{{> child}}\
{{/childs}}\
</ul>';
var partials = {
child:
'<li>\
<a class="opener" href="{{URL}}">{{description}}</a>\
<div class="subnav1 hidden">\
<ul>\
{{#childs}}\
{{> child}}\
{{/childs}}\
</ul>\
</div>\
</li>'
}
jQuery("#subcats").html(Mustache.render(subcat_mustache, data, partials));
$(".opener-parent").parent('li').hover(function (){
$(this).find('subnav1').toggle();
});
});
});
</script>
I have also give class name for upper categories as opener-parent and updated js code
Updated demo: http://jsfiddle.net/R9KLz/4/
Final Result: If you do not want to add opener-parent class, you can use following;
$("#subcats ul li").hover(function (){
if ($(this).find('.subnav1').first().find("ul").has("li").length) {
$(this).find('.subnav1').first().toggle();
}
});
Here is a working demo: http://jsfiddle.net/cubuzoa/R9KLz/5/
Something I noticed, I think you have an extra <div> in your list markup.
<ul class="open-close1" id="reportType">
<li class="active">
<div id="subcats">
<ul>
<li><img class="loading-spinner" src="store/$vendorSettingsDTO.vendorId/assets/themes/$vendorSettingsDTO.skinname/images/loading.gif" height="18"/></li>
</ul>
</div>
<!--</div>-->
</li>
</ul>
Also, I just a hunch, but I think you need to be more specific in which element inside of your class you want to talk to. For example, you have this:
$(this).find('.subnav1').toggle();
But I think you need something more along the lines of:
$(this).find('.subnav1 ul').toggle();
Or, if possible, you could give your list(s) some sort of hook to make it even easier to talk to.

Dynamicly added dropdown menu in AngularJS + Bootstrap

I'm writing a module that will create a dynamic menu on the fly. How to run a directive after adding new <li> with css class dropdown which is also added by ng-class.
The code:
angular.module('myapp', ['ui.bootstrap'])
.factory("menuService", ["$rootScope", function($rootScope) {
"use strict";
return {
menu: function() {
$rootScope.globalMenu;
},
setMenu: function(menu) {
$rootScope.globalMenu = menu;
}
};
}])
.controller("MainController", ["$scope", "menuService",
function($scope, menuService){
menuService.setMenu([{href:"#", label:"Dropdown",
dropdown:[{href:"/edit", label:"Edit"}]},
{href:'/', label:'test'}]);
$scope.bodyText = "Some text";
}]);
This is the code in html
<ul class="navbar-nav nav navbar-left">
<li ng-repeat="menu_element in globalMenu" ng-class="{dropdown: menu_element.dropdown != undefined}">
<a ng-href="{{menu_element.href}}" ng-class="{'dropdown-toggle': menu_element.dropdown != undefined}">
{{menu_element.label}}
<b class="caret" ng-if="menu_element.dropdown != undefined"></b>
</a>
<ul ng-if="menu_element.dropdown != undefined" class="dropdown-menu">
<li ng-repeat="sub_element in $parent.menu_element.dropdown">
<a ng-href="{{sub_element.href}}">{{sub_element.label}}</a>
</li>
</ul>
</li>
</ul>
Link to plunker:
http://plnkr.co/edit/pgH35mmsjLJqV4yJuSYq?p=preview
So what I want to do is the same or similar as for jQuery, there I would run $("li.dropdown").dropdown() after adding whole ul>li blocks. I'm new to Angular and I want to make this in the angular way.
I read about directives, how to use them. But I couldn't find how to apply directive in runtime. I've read about transclude: element in a directive (ui.bootstrap.dropdownToggle) doesn't have it enabled. I'm sure that there is a easy way, but couldn't find it myself...
Solved!
I've finally made it with ng-if and ng-repeat-start. With help in comments, I've found that ng-class does not run directives.
<ul class="navbar-nav nav navbar-left">
<span ng-repeat-start="menu_element in globalMenu"></span>
<li ng-if="menu_element.dropdown !== undefined">
<a ng-href="{{menu_element.href}}" class="dropdown-toggle">
{{menu_element.label}}
<b class="caret" ></b>
</a>
<ul class="dropdown-menu">
<li ng-repeat="sub_element in $parent.menu_element.dropdown">
<a ng-href="{{sub_element.href}}">{{sub_element.label}}</a>
</li>
</ul>
</li>
<li ng-if="menu_element.dropdown === undefined">
<a ng-href="{{menu_element.href}}">
{{menu_element.label}}
</a>
</li>
<span ng-repeat-end></span>
</ul>
Working example on Plnkr. Something happened with the css on Plunker, yesterday it was working... but still it works.
Nice, this helped me along the way. I've a slight variation on your theme.
<ul class="nav navbar-nav">
<li ng-repeat='link in menu track by $index' ng-class='[{dropdown:link.sub}]'>
/* normal menu */
<a ng-if='!link.sub' ng-bind='link.id' ng-click='jump(link.to)'></a>
/* dropdown menu */
<a ng-if='link.sub' class="dropdown-toggle" data-toggle="dropdown">
<span ng-bind='link.id'></span>
<ul class="dropdown-menu inverse-dropdown">
/* and repeat for the submenu */
<li ng-repeat='slink in link.menu track by $index'>
<a ng-bind='slink.id' ng-click='jump(slink.to)'></a>
</li>
</ul>
</a>
</li>
</ul>
My menu array is a list of
{id:'name', to:n}
where n points to an array listing some html I push into the page. When there is a sub menu the menu array element is
{id:'name', sub:true, menu:[{id:'name', to:n}, etc.]}
I tried ui-bootstrap but never got my head around it.

Categories