4

The official documentation at :https://angular-ui.github.io/bootstrap/#/popover says that the following trigger combos can be passed as param to the popover-trigger attribute :

mouseenter: mouseleave
click: click
outsideClick: outsideClick
focus: blur
none

I want to use a combination of

mouseenter: outsideClick

How to achieve this without using the popover-is-open attribute?

Rohit Rane
  • 2,790
  • 6
  • 25
  • 41

2 Answers2

0

You can't, the docs state

The outsideClick trigger will cause the popover to toggle on click, and hide when anything else is clicked.

"anything else" includes the element itself, so toggeling the element using outsideClick on or off and will interfere with the natural behavior of other triggers.

for example if state your triggers like so popover-trigger="mouseleave outsideClick" , the trigger mouseleave will hide the popover instead of showing it if you have already clicked the element, otherwise it will just show it on leave. (plunk).

If you can hack it using popover-is-open then continue doing so, if it bothers you too much you can always request a feature.

svarog
  • 9,477
  • 4
  • 61
  • 77
0

popover-trigger="mouseenter outsideClick" for the uib-popover directive does not seem to work as one would think.

Initially, I thought it meant the following:

  1. On mouse enter show the popover
  2. On mouse leave hide the popover
  3. On click keep popover open in an active state
  4. On outside click close popover if it is in an active state

Since it does not I needed a manual approach, the following is stated in the documentation.

For any non-supported value, the trigger will be used to both show and hide the popover. Using the 'none' trigger will disable the internal trigger(s), one can then use the popover-is-open attribute exclusively to show and hide the popover.

So I created some HTML like:

  <span class="glyphicon glyphicon-info-sign" 
      ng-class="{'text-primary' : isInfoPopoverClicked}"
      ng-click="toggleInfoPopoverClicked()"
      ng-mouseenter="enterInfoPopover()"
      ng-mouseleave="leaveInfoPopover()"
      custom-click-outside="closeInfoPopover()"
      uib-popover-template="'info.html'"
      popover-trigger="'none'"
      popover-is-open="isInfoPopoverOpen()"
      popover-placement="auto top"
      popover-append-to-body="true" >
  </span>

The JS in the controller:

  // Toggle popover's clicked active state
  $scope.toggleInfoPopoverClicked = function() {
    $scope.isInfoPopoverClicked = !$scope.isInfoPopoverClicked;
  };
  // Close the popover, used for outside click and close action inside the template
  $scope.closeInfoPopover = function() {
    delete $scope.isInfoPopoverClicked;
  };
  // On mouse enter, show the popover
  $scope.enterInfoPopover = function() {
    $scope.isInfoPopoverMouseEnter = true;
  };
  // On mouse leave, close the popover.
  // If clicked active state is false set to undefined.
  // This supports when the user clicks the icon to close, 
  // that mouse enter does not immediately display the popover again.
  $scope.leaveInfoPopover = function() {
    $scope.isInfoPopoverMouseEnter = false;
    if(false === $scope.isInfoPopoverClicked) {
      delete $scope.isInfoPopoverClicked;
    }
  };
  // Expression used in the popover-is-open attribute
  $scope.isInfoPopoverOpen = function() {
    if($scope.isInfoPopoverClicked) {
      return true;
    } else if(false === $scope.isInfoPopoverClicked){
      return false;
    }
    return $scope.isInfoPopoverMouseEnter;
  };

The template for the uib-popover-template I used:

  <div custom-stop-event="click" class="pull-right">
   <span ng-click="closeInfoPopover()" class="glyphicon glyphicon-remove"></span>
   <section>{{info}}</section>
  </div>

Now the trickier part was that this solution required me to create two more directives.

  1. One to close the popover when clicking outside the element.
  2. Another to stop the click event fired inside the pop-up. Preventing it from closing the popover.

The custom-click-outside directive:

angular.module('LSPApp').directive('customClickOutside', ['$document', function ($document) {
return {
    restrict: 'A',
    scope: {
        clickOutside: '&customClickOutside'
    },
    link: function (scope, element) {
        var handler = function (event) {
            if (element !== event.target && !element[0].contains(event.target)) {
                scope.$applyAsync(function () {
                    scope.clickOutside();
                });
            }
        };

        // Might not work on elements that stop events from bubbling up
        $document.on('click', handler);

        // Clean up event so it does not keep firing after leaving scope
        scope.$on('$destroy', function() {
            $document.off('click', handler);
        });
    }
};
}]);

The custom-stop-event directive called from the template's HTML:

angular.module('LSPApp').directive('stopEvent', function () {
return {
  restrict: 'A',
  link: function (scope, element, attr) {
    element.on(attr.stopEvent, function (e) {
      e.stopPropagation();
    });
  }
};
});

Hopefully, this helps someone, my final solution had all this encapsulated in it's own directive to promote reuse.

Gabe Gates
  • 902
  • 1
  • 14
  • 19