Menu and other submenus when pressed
I am trying to create menus and submenus in angular. What I want to do is to have two arrays of Menu objects
menu = [{name: 'Name1', link: '/link1'}, {name: 'Name2', link: '/link2'}]
submenu = [[{name: 'SubName1', link: '/Sublink1'}, {name: 'SubName1', link: '/sublink1'}],
[[{name: 'SubName2', link: '/Sublink2'}, {name: 'SubName2', link: '/sublink2'}]]
So when I click Name1
, the first array will be selected SubMenu
, and by pressing Name2
the second array is selected. How can I create two Directives for the main menu and the second one for the second and be able to communicate between them on click. I tried to build this in the controller, I was able to select the submenu with $index
, but the submenu cannot be moved as I like because it has to be under the controller.
I finally managed to solve my problem, here is the solution: http://jsfiddle.net/4kjjyL4s/4/
How can I improve my solution?
source to share
Don't reinvent the wheel :) The UI router is a prepackaged solution that handles nested routing for you.
If you have a menu of items and you want to display a different menu of items when one of the items is selected, the UI router does just that. https://github.com/angular-ui/ui-router
source to share
It is impossible to give an exact answer because the information is missing, but for example if you use directives with different menu items elsewhere in your application, I would recommend passing the menu array from the controller (ng -controller, not directive controller) through the scope.
Also, if you're looking for a standard way for directives to communicate directly (in your case, the link between a menu and a submenu directive to notify an item selection change), use a directive controller. Here's a good tutorial.
https://thinkster.io/egghead/directive-to-directive-communication/
source to share
For communication between controllers or directives, you must use services .
From the angular manual ( https://docs.angularjs.org/guide/services ):
Angular services are replaceable objects that are chained together using Dependency Injection (DI). You can use services to organize and share code in your application.
I checked the code you posted on the jsfiddle ( http://jsfiddle.net/4kjjyL4s/4/ ) and I tried to make the most of it. Below are my changes to the JavaScript file (please read the comments in the code).
var app = angular.module("app",[]);
app.controller('main', function(){});
// The service will be responsible for the shared objects and logic
app.service('MenuService', function () {
var list = [
{
name: "Menu1", link: "#menu1",
submenu: [
{ name: "Menu1Sub1", link: "#submenu1" },
{ name: "Menu1Sub2", link: "#submenu2" }
]
},
{
name: "Menu2", link: "#menu2",
submenu: [
{ name: "Menu2Sub1", link: "#submenu1" },
{ name: "Menu2Sub2", link: "#submenu2" }
]
}
];
var selected = [];
// methods and attributes published under the **this**
// keyword will be publicly available
this.getMenu = function () {
return list;
};
this.getSubmenu = function () {
return selected;
};
this.select = function ( menuItem ) {
// this does the *trick*!
// if we use the assignment operator here, we would replace the
// reference returned in the getSubmenu() method, so as the original
// reference did not change, angular dirty checking would not detect it.
// using angular.copy() method, we are copying the contents of the
// selected submenu over the same reference returned by getSubmenu()
angular.copy( menuItem.submenu, selected );
};
});
// No $rootScope injection results in better re-usability. When you were
// relying in $rootScope sharing, both directives should live in the
// $rootScope, so if you add them inside a ng-controller created scope
// they would not work anymore
app.directive("menu", function() {
return {
restrict: "E",
// no need to isolate scope here, *scope:true* creates a new scope
// which inherits from the current scope
scope: true,
// with controllerAs (introduced in angular 1.2), you can skip
// polluting the scope injection.
controllerAs: "ctrl",
controller: function( MenuService ) {
this.list = MenuService.getMenu();
this.changeSub = function ( menuItem ) { MenuService.select( menuItem ); };
},
template: "<div ng-repeat='menu in ctrl.list'><button ng-click='ctrl.changeSub(menu)'>{{menu.name}}</button></div>"
};
});
app.directive("submenu", function() {
return {
restrict: "E",
scope: true,
controllerAs: "ctrl",
controller: function( MenuService ) {
this.sublist = MenuService.getSubmenu();
},
template: "<span ng-repeat='menu in ctrl.sublist'>{{menu.name}} | </span>aa"
};
});
And here is an updated HTML file to show that both directives are now not directly inserted into $rootScope
<div ng-app="app">
<div ng-controller="main">
<menu></menu>
<h1>Hello World!</h1>
<div class="main-content">
<submenu></submenu>
</div>
</div>
</div>
Hope it helps!
source to share
Try this code:
function MyCtrl ($scope) {
$scope.subMenu = []; // default is false
$scope.toggleSubMenu = function (index) {
$scope.subMenu[index] = !$scope.subMenu[index];
};
}
Html
<ul>
<li ng-class="{active: subMenu[0]}"> <a href="#hello" ng-click="toggleSubMenu(0)">Name1</a>
<ul>
<li>test</li>
<li>test</li>
<li>test</li>
</ul>
</li>
<li ng-class="{active: subMenu[1]}"> <a href="#foo" ng-click="toggleSubMenu(1)">Name2</a>
<ul>
<li>bar</li>
<li>bar</li>
<li>bar</li>
</ul>
</li>
</ul>
Also check this
source to share