如何使用 AngularJS 处理文档单击并通知其他控制器
How to handle document click and notify other controllers using AngularJS?
我使用AngularJS创建了一个水平下拉菜单。
菜单部分由一个名为menuController的角度控制器管理。实现了标准菜单行为,因此在悬停时突出显示主菜单项,除非将其禁用。单击主菜单项时,子菜单将切换。如果子菜单处于打开状态,我希望当用户单击文档上的任何其他位置时它消失。我试图创建一个指令来侦听文档单击事件,但不确定如何通知菜单控制器。我应该如何以 AngularJS 的方式实现此方案?
部分工作的原始 Plunk,没有文档点击处理机制。
更新:
根据回答的建议,我采用了 Brodcast 方法并更新了脚本以反映我的最新更改。它正在按照我的期望工作。我使全局控制器$broadcast一条消息,菜单控制器订阅该消息。
更新 2:修改了注入全局事件定义数据的代码。
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;
});
}
]);
更新 3:修改了先前实现中的代码,以修复文档单击多次触发的错误。几乎类似的方法,但这一次,如果有人再次单击菜单上的任意位置,则忽略该单击。有关完整的代码示例,请参阅新的工作 Plunk
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
});
});
}
);
}
};
}
]);
更新 4:实施了另一个替代选项。有关完整代码示例,请参阅选项 2 Plunk
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);
});
});
}
]);
您可以将指令简化为如下所示的内容:
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);
});
}
};
}
]);
然后将一个函数从菜单控制器传递给它:
<section class="local-nav" ng-controller="menuController" on-document-click="someFunction()">
这样不需要全局控制器。
如果要保留全局控制器并从那里处理它,则可以:
1.) 将菜单变成服务,然后将其注入到需要能够控制它的所有控制器中。
2.) 从全局控制器广播事件并在菜单控制器中侦听它。
具体的替代解决方案:您可以将指令转换为"外部元素单击"并像这样使用它:
<ul on-outside-element-click="closeMenus()">
该指令如下所示,只有在ul
外部单击时才会调用closeMenus()
:
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);
});
}
};
}
]);
工作普伦克:http://plnkr.co/edit/zVo0fL2wOCQb3eAUx44U?p=preview
你做得很好。如果对menuController
应用相同的指令
<section class="local-nav" ng-controller="menuController" on-global-click="handleClick($event)>
并在您的menuController
中定义点击处理程序,您就可以开始了。
我认为在文档上为事件设置多个处理程序没有任何害处。因此,无论您在哪里定义此指令,该元素都可以响应全局文档单击事件。
更新:当我对此进行测试时,它会导致另一个问题,无论您单击页面在哪里,都会调用此方法。您现在需要一种区分机制。
- 如何添加浮动和非浮动,其他
- 与其他库的jQuery.noConflict()
- 播放当前视频时如何停止其他视频?JavaScript
- 剑道UI内联编辑:如何在点击其他按钮时隐藏按钮
- 制作一个不带HTML a标记但在动画播放完毕后指向其他页面的超链接
- Firebase2(Firebase.google.com)推送通知-从外部管理
- 除修剪外的其他功能
- 是什么让一个“;Uncaught RangeError:超过了最大调用堆栈大小“;错误(Chrome,在其他浏览器中显示
- Array.length似乎不起作用;console.log则显示其他情况
- 如何消除代码中的未定义和其他问题
- 如何覆盖锚点元素's href目标,并在我点击转到目标javascript时删除其他错误
- jquery代码在Mozilla中有效,但在其他浏览器上无效
- 为什么不推荐使用“with”?是否有更好或其他方法可以“下降”到对象的命名空间
- 向其他用户推送通知
- 如何使用 AngularJS 处理文档单击并通知其他控制器
- 如何将数据(如用户 ID)传递给 Web 工作人员,以便从服务器获取其他推送通知数据
- 如何在单击 Webkit 通知时从浏览器中的任何其他选项卡移回当前选项卡
- Phonegap android 向其他应用程序用户发送通知
- 当indexedDB条目在其他选项卡中更改时获得通知
- 是否有可能在其他显示器上显示网页推送通知而不是主显示器