防止执行两个指令表达式

Prevent execution of two directive expressions

我有这个html

<nav ng-init="items = false">
    <ul class="main-items">
        <li><a ng-click="..."></a></li>
        <li><a ng-click="..."></a></li>
        ...
        <li><a ng-click="items = !items">More...</a></li>
    </ul>
    <ul class="more-items" ng-show="items" doc-mousedown="$parent.items = false">
        <li><a ng-click="...">Item 1</a></li>
        <li><a ng-click="...">Item 2</a></li>
        ...
    </ul>
</nav>

这是做什么的:

  1. .main-items 始终可见,当单击 更多... 时,它会切换 more-items.
  2. 的显示
  3. doc-click 是我自定义的简单指令,用于侦听文档 mousedown,它检查鼠标是否在 之外进行交互定义指令并执行提供的表达式的元素。在这种情况下,它隐藏了 .more-items.

问题

单击 更多... link 有效并正确显示其他项目,但是当再次单击时,此元素在元素之外指令因此指令首先触发,然后 更多... link 上的 ngClick 显示其他项目,而不是仅仅隐藏它们...

我正在尝试解决这个问题,但我似乎无法解决这个问题。我想在 docMousedown 上使用 $event.preventDefault(),但是 mousedown 和 click 是两个不共享事件对象的事件。 :(

你建议如何解决这个问题?

工作示例

DocDetectController.$inject = ["$scope", "$element", "$document"];

function DocDetectController($scope, $element, $document) {
  var el = $element[0];
  var self = this;
  
  $document.on("mousedown.docDetect", function(evt) {
    if (evt.target !== el && !$.contains(el, evt.target)) {
      evt.preventDefault();
      $scope.$apply(self.docDetect);
    }
  });
}

angular
  .module("Test", [])
  .directive("docDetect", function() {
    return {
      restrict: "A",
      controller: DocDetectController,
      controllerAs: "doc",
      scope: true,
      bindToController: {
        docDetect: "&"
      }
    };
  });
nav ul {
  display: flex;
  list-style: none;
  margin: 0;
  padding: 0;
  user-select: none;
}
nav ul li {
  margin: 0;
  padding: 0;
}
nav ul li a {
  text-decoration: none;
  padding: .5rem 1rem;
  background-color: #f4f4f4;
  color: #000;
}
nav ul li a:hover {
  background-color: #ddd;
}

nav.collapsed .more-items {
  display: none;
}

hr {
  height: 1px;
  border: 0 none;
  background-color: #ccc;
}
<section ng-app="Test">
  <nav ng-class="{ collapsed: collapse }" ng-init="collapse = true">
    <ul class="main-items">
      <li><a href="">Link 1</a></li>
      <li><a href="">Link 2</a></li>
      <li><a href="">Link 3</a></li>
      <li><a href="" ng-click="collapse = !collapse">More...</a></li>
    </ul>
    <hr>
    <ul class="more-items" doc-detect="$parent.collapse = true">
      <li><a href="">More 1</a></li>
      <li><a href="">More 2</a></li>
      <li><a href="">More 3</a></li>
      <li><a href="">More 4</a></li>
      <li><a href="">More 5</a></li>
    </ul>
  </nav>
</section>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>

有一个简单的方法可以做到这一点你需要检查这个解决方案的目标 在你的 child 事件中,你只需要添加一个检查,你将确保如果目标是 parent 元素不会触发任何东西,在这种情况下你需要从它被触发的地方检查元素您可以在您的活动 object.

中获取这些详细信息

最简单的解决方案

是根据 $scope.items 值改变 更多... 行为的那个。

HTML 模板

<nav ng-init="items = false">
    <ul class="main-items">
        <li><a ng-click="..."></a></li>
        <li><a ng-click="..."></a></li>
        ...
        <!-- THIS IS THE CHANGE -->
        <li ng-switch="!!items">
            <a ng-switch-when="false" ng-click="items = !items">More...</a>
            <a ng-switch-when="true" href="">More...</a>
        </li>
    </ul>
    <ul class="more-items" ng-show="items" doc-mousedown="$parent.items = false">
        <li><a ng-click="...">Item 1</a></li>
        <li><a ng-click="...">Item 2</a></li>
        ...
    </ul>
</nav>

DocDetectController.$inject = ["$scope", "$element", "$document"];

function DocDetectController($scope, $element, $document) {
  var el = $element[0];
  var self = this;
  
  $document.on("mousedown.docDetect", function(evt) {
    if (evt.target !== el && !$.contains(el, evt.target)) {
      evt.preventDefault();
      $scope.$apply(self.docDetect);
    }
  });
}

angular
  .module("Test", [])
  .directive("docDetect", function() {
    return {
      restrict: "A",
      controller: DocDetectController,
      controllerAs: "doc",
      scope: true,
      bindToController: {
        docDetect: "&"
      }
    };
  });
nav ul {
  display: flex;
  list-style: none;
  margin: 0;
  padding: 0;
  user-select: none;
}
nav ul li {
  margin: 0;
  padding: 0;
}
nav ul li a {
  text-decoration: none;
  padding: .5rem 1rem;
  background-color: #f4f4f4;
  color: #000;
}
nav ul li a:hover {
  background-color: #ddd;
}

nav.collapsed .more-items {
  display: none;
}

hr {
  height: 1px;
  border: 0 none;
  background-color: #ccc;
}
<section ng-app="Test">
  <nav ng-class="{ collapsed: collapse }" ng-init="collapse = true">
    <ul class="main-items">
      <li><a href="">Link 1</a></li>
      <li><a href="">Link 2</a></li>
      <li><a href="">Link 3</a></li>
      <li ng-switch="!!collapse">
        <a ng-switch-when="true" href="" ng-click="$parent.collapse = !$parent.collapse">More...</a>
        <a ng-switch-when="false" href="">More...</a>
      </li>
    </ul>
    <hr>
    <ul class="more-items" doc-detect="$parent.collapse = true">
      <li><a href="">More 1</a></li>
      <li><a href="">More 2</a></li>
      <li><a href="">More 3</a></li>
      <li><a href="">More 4</a></li>
      <li><a href="">More 5</a></li>
    </ul>
  </nav>
</section>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>