How to handle document clicks and notify other controllers using AngularJS?

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); }); }); } ]); 
+7
javascript angularjs
source share
2 answers

You can simplify the directive like this:

 changeNotificationApp.directive('onDocumentClick', ['$document', function($document) { return { restrict: 'A', link: function(scope, element, attrs) { var onClick = function() { scope.$apply(function() { scope.$eval(attrs.onDocumentClick); }); }; $document.on('click', onClick); scope.$on('$destroy', function() { $document.off('click', onClick); }); } }; } ]); 

Then pass the function from the Controller menu:

 <section class="local-nav" ng-controller="menuController" on-document-click="someFunction()"> 

There is no need for a global controller in this way.

If you want to save the globalController and process it from there, you can:

1.) Make a menu in the service, and then enter it into all the controllers that should be able to manage it.

2.) Pass the event from globalController and listen to it in menuController.

Specific alternative solution: you can turn the directive into "on-outside-element-click" and use it as follows:

 <ul on-outside-element-click="closeMenus()"> 

The directive looks like this and will only call closeMenus() if you press << 24>:

 changeNotificationApp.directive('onOutsideElementClick', ['$document', function($document) { return { restrict: 'A', link: function(scope, element, attrs) { element.on('click', function(e) { e.stopPropagation(); }); var onClick = function() { scope.$apply(function() { scope.$eval(attrs.onOutsideElementClick); }); }; $document.on('click', onClick); scope.$on('$destroy', function() { $document.off('click', onClick); }); } }; } ]); 

Work plunker: http://plnkr.co/edit/zVo0fL2wOCQb3eAUx44U?p=preview

+17
source share

Ok, you did a good job. If you apply the same directive to menuController

 <section class="local-nav" ng-controller="menuController" on-global-click="handleClick($event)> 

and you have a click handler defined in your menuController that you will configure.

I don’t think there is harm in having several event handlers. Therefore, whenever you specify this directive, this element can respond to a global document click event.

Update . As I tested this, this leads to another problem when this method is called when you click on the page. You need a mechanism for differentiation.

+1
source share

All Articles