I created a horizontal dropdown using AngularJS.
The menu section is controlled by an angular controller called menuController. The standard behavior of the menu is implemented, so when you hover over the main menu item, it lights up if it is not disabled. When you click on the main menu item, the submenu switches. If the Sub-menu is in the open state, I want it to leave when the user clicks elsewhere in the document. I tried to create a directive to listen for a document click event, but am not sure how to notify the menu manager of this. How to implement this script using AngularJS method?
Partially works Original Plunk without the mechanism of processing document clicks.
UPDATE:
Based on the answer I received, I went with the Brodcast approach and updated the script to reflect my latest changes. It works according to my expectations. I made globalController $ a broadcast message, and menuController subscribed to this message.
UPDATE 2: Modified code for entering global event definition data.
var eventDefs = (function() { return { common_changenotification_on_document_click: 'common.changenotification.on.document.click' }; }()); var changeNotificationApp = angular.module('changeNotificationApp', []); changeNotificationApp.value('appEvents', eventDefs); changeNotificationApp.directive("onGlobalClick", ['$document', '$parse', function($document, $parse) { return { restrict: 'A', link: function($scope, $element, $attributes) { var scopeExpression = $attributes.onGlobalClick; var invoker = $parse(scopeExpression); $document.on("click", function(event) { $scope.$apply(function() { invoker($scope, { $event: event }); }); } ); } }; } ]); changeNotificationApp.controller("globalController", ['$scope', 'appEvents', function($scope, appEvents) { $scope.handleClick = function(event) { $scope.$broadcast(appEvents.common_changenotification_on_document_click, { target: event.target }); }; } ]); //menu-controller.js changeNotificationApp.controller('menuController', ['$scope', '$window', 'appEvents', function($scope, $window, appEvents) { $scope.IsLocalMenuClicked = false; $scope.menu = [{ Name: "INTEGRATION", Tag: "integration", IsDisabled: false, IsSelected: false, SubMenu: [{ Name: "SRC Messages", Tag: "ncs-notifications", IsDisabled: false, AspNetMvcController: "SearchSRCMessages" }, { Name: "Target Messages", Tag: "advisor-notifications", IsDisabled: false, AspNetMvcController: "SearchTaregtMessages" }] }, { Name: "AUDITING", Tag: "auditing", IsDisabled: true, IsSelected: false, SubMenu: [] }]; $scope.appInfo = { Version: "1.0.0.0", User: "VB", Server: "azzcvy0623401v", IsSelected: false }; var resetMenu = function() { angular.forEach($scope.menu, function(item) { item.IsSelected = false; }); $scope.appInfo.IsSelected = false; }; $scope.toggleDropDownMenu = function(menuItem) { var currentDropDownState = menuItem.IsSelected; resetMenu($scope.menu, $scope.appInfo); menuItem.IsSelected = !currentDropDownState; $scope.IsLocalMenuClicked = true; }; $scope.loadPage = function(menuItem) { if (menuItem.AspNetMvcController) $window.location.href = menuItem.AspNetMvcController; }; $scope.$on(appEvents.common_changenotification_on_document_click, function(event, data) { if (!$scope.IsLocalMenuClicked) resetMenu($scope.menu, $scope.appInfo); $scope.IsLocalMenuClicked = false; }); } ]);
UPDATE 3: Modified code in a previous implementation to fix a bug where clicking a document causes several times. Almost the same approach, but this time, if someone clicks again anywhere in the menu, the click is ignored. Please refer to New Working Plunk for an example with full code.
changeNotificationApp.directive("onGlobalClick", ['$document', '$parse', function ($document, $parse) { return { restrict: 'A', link: function ($scope, $element, $attributes) { var scopeExpression = $attributes.onGlobalClick; var invoker = $parse(scopeExpression); $document.on("click", function (event) { var isClickedElementIsChildOfThisElement = $element.find(event.target).length > 0; if (isClickedElementIsChildOfThisElement) return; $scope.$apply(function () { invoker($scope, { $event: event }); }); } ); } }; } ]);
UPDATE 4: Another alternative option implemented. Please refer to Option 2 of Plunk for an example with full code.
var eventDefs = (function () { return { on_click_anywhere: 'common.changenotification.on.document.click' }; }()); var changeNotificationApp = angular.module('changeNotificationApp', []); changeNotificationApp.value('appEvents', eventDefs); changeNotificationApp.directive("onClickAnywhere", ['$window', 'appEvents', function($window, appEvents) { return { link: function($scope, $element) { angular.element($window).on('click', function(e) { // Namespacing events with name of directive + event to avoid collisions $scope.$broadcast(appEvents.on_click_anywhere, e.target); }); } }; } ]); //menu-controller.js changeNotificationApp.controller('menuController', ['$scope', '$window', 'appEvents', '$element', function ($scope, $window, appEvents, $element) { $scope.menu = [ { Name: "INTEGRATION", Tag: "integration", IsDisabled: false, IsSelected: false, SubMenu: [ { Name: "SRC Messages", Tag: "ncs-notifications", IsDisabled: false, AspNetMvcController: "SearchSRCMessages" }, { Name: "Target Messages", Tag: "advisor-notifications", IsDisabled: false, AspNetMvcController: "SearchTaregtMessages" } ] }, { Name: "AUDITING", Tag: "auditing", IsDisabled: true, IsSelected: false, SubMenu: [] } ]; $scope.appInfo = { Version: "1.0.0.0", User: "VB", Server: "azzcvy0623401v", IsSelected: false }; var resetMenu = function () { angular.forEach($scope.menu, function (item) { item.IsSelected = false; }); $scope.appInfo.IsSelected = false; }; $scope.toggleDropDownMenu = function (menuItem) { var currentDropDownState = menuItem.IsSelected; resetMenu($scope.menu, $scope.appInfo); menuItem.IsSelected = !currentDropDownState; }; $scope.loadPage = function (menuItem) { if (menuItem.AspNetMvcController) $window.location.href = menuItem.AspNetMvcController; }; $scope.$on(appEvents.on_click_anywhere, function(event, targetElement) { var isClickedElementIsChildOfThisElement = $element.find(targetElement).length > 0; if (isClickedElementIsChildOfThisElement) return; $scope.$apply(function(){ resetMenu($scope.menu, $scope.appInfo); }); }); } ]);