Избегайте самопереключения через распространение событий в директиве

У меня есть директива всплывающей подсказки, которая должна открываться/закрываться при щелчке и дополнительно закрываться при произвольном щелчке. Поскольку angular-translate не позволяет изменять содержимое самого элемента, вместо этого я добавляю элементы директивы.

Директива выглядит примерно так (учтите, что это упрощенная версия):

{
    restrict: 'A',
    scope: {},
    compile: function(element)
    {
        element.after('<div class="toggle"></div>
            <div class="message" ng-show="active"></div>');
    },
    link: function(scope, element)
    {
        angular.element(element.siblings('toggle')).click(function()
        {
            scope.$apply(function()
            {
                scope.active = !scope.active;
            }
        });
        angular.element(document).click(function()
        {
            scope.$apply(function()
            {
                scope.active = false;
            }
        });
    } 
}

Проблема в том, что событие щелчка по элементу запускается (scope.active = true), а затем распространяется на документ, в то время как он мгновенно закрывается.

Однако, если я останавливаю распространение, в большинстве случаев это работает нормально, единственный случай, когда это не так, - это когда у меня есть несколько экземпляров директивы на моей странице, и я нажимаю один за другим (первый открытый должен закрыться, а другой должен открыться но из-за остановки распространения событие document.click другого экземпляра не срабатывает.

Редактировать: Эта скрипка демонстрирует проблему. Если вы щелкнете зеленое поле и щелкнете его еще раз, оно сработает, если вы щелкнете зеленое поле, а затем фон, он сработает, если вы щелкнете зеленое поле, а затем другое зеленое поле, однако оба открыты, хотя первое должно закрыться.
Если вы удалите event.stopPropagation() на line 20, подсказка вообще не будет отображаться, поскольку сначала срабатывает щелчок по окну, подсказка отображается, но после дальнейшего распространения события также срабатывает щелчок по фону и мгновенно снова закрывает его.


Если есть лучшее решение моей проблемы, я тоже буду признателен


person Aides    schedule 30.03.2016    source источник
comment
Можете ли вы привести пример jsfiddle, с помощью которого можно воспроизвести проблему?   -  person Stepan Kasyanenko    schedule 31.03.2016
comment
Конечно, проверьте обновленный вопрос   -  person Aides    schedule 31.03.2016


Ответы (1)


Это одно из решений, которое вам поможет.

Живой пример на jsfiddle.

angular.module('app', [])
  .controller('testCtrl', ['$scope',
    function($scope) {
      var self = this;
    }
  ])
  .directive('hintDirective', ['$compile',
    function($compile) {
      var directiveHint = [];
      return {
        restrict: 'A',
        transclude: true,
        scope: {},
        template: '<ng-transclude></ng-transclude><span ng-click="toggle($event)" class="toggle"></span><div class="message" ng-show="active">Hint Message</div>',
        link: function(scope, element) {
          scope.active = false;
          directiveHint.push(scope);
          var span = element.find('span')[1];
          scope.toggle = function($ev) {
            scope.active = !scope.active;
            closeOther(scope);
            $ev.stopPropagation();
          }

          function closeOther(scope) {
            angular.forEach(directiveHint, function(sc) {
              if (sc != scope) {
                closeActive(sc)
              }
            });
          }

          function closeAll() {
            angular.forEach(directiveHint, function(sc) {
              closeActive(sc)
            });
          }

          function closeActive(sc) {
            if (sc.active)
              sc.active = false;
          }

          function documentClick(event) {
            scope.$apply(function() {
              closeAll();
            });
          }
          var $body = angular.element(document.body);
          if (!$body.hasClass('hint-directive-click')) {
            angular.element(document).bind('click', documentClick);
            $body.addClass('hint-directive-click');
          }

          scope.$on('$destroy', function() {
            angular.forEach(directiveHint, function(sc, i) {
              if (sc == scope)
                directiveHint.splice(i);
            });
          })
        }
      }
    }
  ]);
html,
body {
  width: 100%;
  height: 100%;
}
.split {
  float: left;
  width: 50%;
  height: 100%;
}
.toggle {
  display: inline-block;
  width: 1em;
  height: 1em;
  background-color: green;
}
p {
  float: left;
}
.message {
  background-color: grey;
  width: 200px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<div ng-app="app">
  <div class="split">
    <br>
    <br>
    <br>
    <br>
    <br>
    <p hint-directive>
      Text1
    </p>
  </div>
  <div class="split">
    <br>
    <br>
    <br>
    <br>
    <br>
    <p hint-directive>
      Text2
    </p>
    <label>
      <input type="checkbox" ng-model="text3Enable">Enable text3</label>
    <p hint-directive ng-if="text3Enable">
      Text3
    </p>
  </div>
</div>

person Stepan Kasyanenko    schedule 31.03.2016