在指令中测试点击主体的最角度方法是什么?

时间:2014-10-20 09:19:04

标签: angularjs angularjs-directive

我有一个小的下拉指令,我应用于nav元素。如果用户通过设置$scope.menu.visible = false点击导航元素之外的任何位置,我想关闭下拉列表。

从Angular指令中检查body元素上的click或keypress的最Angular方法是什么?

我目前的指令如下:

angular.module('dropdowns', [])
  .directive('dropdown', function () {
    return {
      scope: true,
      link: function (scope, element) {
        var body = angular.element('body');
        var clickOutside = function (e) {
          var target = angular.element(e.target);
          if (!$.contains(element, target)) {
            scope.menu.visible = false;
          }
        }
        body.on('click', clickOutside);
        // body.on('keyup', scope.escapeKey);
      },
      controller: function($scope) {
        $scope.menu = {visible: false};
        $scope.hide = function () {
          $scope.menu.visible = false;
        };

        $scope.show = function () {
          scope.menu.visible = true;
        };

        $scope.toggle = function (e) {
          $scope.menu.visible = !$scope.menu.visible;
        };

        $scope.escapeKey = function (e) {
          if (e.which === 27) {
            $scope.hide();
          }
        };
      }
    };
  });

使用以下模板片段设置下拉列表:

<nav dropdown>
  <a ng-click="toggle()">Menu</a>
  <ul ng-show="menu.visible">
    ...
  </ul>
</nav>

6 个答案:

答案 0 :(得分:2)

最角度的方式: 将其添加到指令中。示例:将click-events属性添加到&#39; body&#39;标签 类似的东西:

angular.module('directive_name', []).directive('clickEvents', [
    '$rootScope',
    '$document',
  function($rootScope, $document) {
    return {
      link: function() {
        $document.bind('click', function(event) {
          $rootScope.$broadcast('click', event);
        });
      }
    };
  }
]);

module.directive('directive_name', [
  function() {
    return {
      link: function(scope, element, attributes) {
        scope.$on('click', function(onEvent, clickEvent) {
          scope.clicked = true;
        });
      }
    };
  }
]);

答案 1 :(得分:1)

<body ng-click="yourFunction()">

似乎是明显的选择。但是,请确保ng-app指令在<html>上,以便在应用中包含<body>

答案 2 :(得分:1)

不知道&#34;最角度的方式&#34;,但也许最容易。 假设你的模板中有这样的东西。

<ul class="dropdown-menu keep_open">
    <li>....

然后在您的指令linkcontroller中,只需添加以下功能

/*  forces dropdown to remain open until clicked outside the control */
$('.keep_open').click(function(event){
    event.stopPropagation();
});

此处相关的plunker http://plnkr.co/edit/tj2ljc

<小时/> imgur

答案 3 :(得分:1)

你的实现看起来很不错,但我真的会研究下拉列表的Bootstrap实现。

https://github.com/angular-ui/bootstrap/blob/master/src/dropdown/dropdown.js

答案 4 :(得分:1)

您需要做的就是在下拉指令元素级别停止事件传播。轮到它的身体元素将处理进入它的点击事件。以下是您的指令的链接功能:

link: function(scope, element) {

    var body = angular.element('body');

    body.on('click', function(e) {
        scope.$apply(function() {
            scope.menu.visible = false;
        });
    });

    element.on('click', function(e) {
        e.stopPropagation();
    });

    // body.on('keyup', scope.escapeKey);
},

演示:http://plnkr.co/edit/aUrztdwKL4ze4Vt3qihF?p=preview

答案 5 :(得分:0)

我会分享我的最终代码,以防它对其他任何人有用。有一些很好的提示,例如从rootScope广播并使用stopPropagation。我最终得到了类似的东西。当单击下拉列表中未包含的任何元素或按下转义键时,它将关闭。

我最终在闭包中保留了对body的引用,因此每个下拉列表都保留了它自己的引用,它仍然需要一个select,但是变得自包含,这是一个奖励。

angular.module('dropdowns', [])
  .directive('dropdown', function () {
    return {
      scope: true,
      link: function (scope, element) {

        scope.menu = {
          visible: false
        };

        scope.hide = function () {
          scope.menu.visible = false;
        };

        scope.show = function () {
          scope.menu.visible = true;
        };

        scope.toggle = function (e) {
          scope.menu.visible = !scope.menu.visible;
        };

        var escapeKey = function (e) {
          if (e.which === 27) {
            scope.hide();
            scope.$apply();
          }
        };

        var clickOutside = function (e) {
          if (!$.contains(element[0], e.target)) {
            scope.hide();
            scope.$apply();
          }
        }
        var body = angular.element('body');
        body.on('click', clickOutside);
        body.on('keyup', escapeKey);

      }
    };
  });