Angular UI Popover close button

2019-08-08 03:00发布

问题:

I've read Angular UI Bootstrap adding a close button and show hidden div on ng-click within ng-repeat. I'd like to use the solution from the latter article and apply it to the problem stated in the first article. In essence, I want to be able to close an Angular UI Bootstrap popover with ng-show or ng-click.

I have an example piece of code to illustrate this. This code just applies a CSS class to a particular element whenever it is clicked, and removes it when it is clicked again:

<div ng-class="{'gray-inset-border': style}">
    <div ng-click="style=!style"></div>
</div>

Whenever an element containing a popover is clicked, a popover template is created. In the Chrome DOM inspector, the opening tag looks like this:

<div class="popover ng-isolate-scope right fade in"
tooltip-animation-class="fade" tooltip-classes="" 
ng-class="{ in: isOpen() }" popover-template-popup="" title="" 
content-exp="contentExp()" placement="right" popup-class=""  animation="animation" 
is-open="isOpen" origin-scope="origScope" 
style="top: 317.5px; left: 541.8125px; display: block;">

Notice the ng-class="{in: isOpen()}". I am assuming that this controls whether the popover is open or not, and want to use the same ng-click method as in the example above, and apply it to a button within the popover. However, when I tried that, it didn't work. I also can't find the popover template anywhere in the ui-bootstrap-tpls.js code. As far as I know, popover creation is voodoo magic.

It's also frustrating that Angular UI Bootstrap doesn't have this functionality already. I've been trying to solve this problem off and on for over a week now, and every "solution" I have seen doesn't seem to work for me.

Am I understanding the ng-class="{in: isOpen()}" correctly? Where do I edit the popover template to add a close button?

回答1:

This was solved by @ognus on a GitHub thread.

He stated:

I've found that using a simple custom directive worked best for my use case. I just want to be able to close the popover from within the popover template.

The directive exposes scope.toggle method that user custom trigger to open and close popover. I'm then using this method in the popover template.

There is a plnkr that I adapted to test my own issue. The solution involved creating a directive (of course).

HTML

<!DOCTYPE html>
<html ng-app="main">

  <head>
    <script data-require="angular.js@1.x" data-semver="1.4.1" src="https://code.angularjs.org/1.4.1/angular.js"></script>
    <script data-require="ui-bootstrap@0.13.0" data-semver="0.13.0" src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.13.0.min.js"></script>
    <link data-require="bootstrap-css@*" data-semver="3.3.1" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" />
        <script src="popoverToggle.js"></script>
    <script src="script.js"></script>

  </head>

  <body style="margin: 50px">
    <!-- Show popover link -->
    <a
      href=""
      popover-placement="bottom"
      popover-trigger="open"
      popover="Lorem ipsum dolor sit amet, consectetur."
      popover-title="This is a title"
      popover-toggle>
      Show popover</a>

    <div popover-placement="bottom" popover-trigger="open" 
    popover-template="'popover-template.html'" popover-toggle>Show Popover 2</div>


  </body>
</html>

popoverToggle directive

angular.module('main')

.config(function($tooltipProvider) {
  $tooltipProvider.setTriggers({'open': 'close'});
})

.directive('popoverToggle', function($timeout) {
  return {
    scope: true,
    link: function(scope, element, attrs) {
      scope.toggle = function() {
        $timeout(function() {
          element.triggerHandler(scope.openned ? 'close' : 'open');
          scope.openned = !scope.openned;
        });
      };
      return element.on('click', scope.toggle);
    }
  };
});

Popover template

<p>Are you sure you want to remove this item?</p>
<a href='' ng-click='remove(item)'>Yes</a> 
<div ng-click='toggle()'>No</div>


回答2:

app = angular.module('ui.bootstrap.demo', ['ui.bootstrap']);

app.controller( 
  'dataCtrl', function() {
    var self = this;
    self.data = [
      {name: "one", num: 23},
      {name: "two", num: 87},
      {name: "three", num: 283}
    ]
    return self;
  }
)

app.controller(
  'myPopoverCtrl', ['$scope',
    function($scope) {

      // query popover
      $scope.myPopover = {

        isOpen: false,

        templateUrl: 'myPopoverTemplate.html',

        open: function open( value ) {
          $scope.myPopover.isOpen = true;
          $scope.myPopover.data = "(" + value.num + ")";
        },

        close: function close() {
          $scope.myPopover.isOpen = false;
        }
      };

    }

  ]);
<body ng-app="ui.bootstrap.demo" class='container'>
  <div ng-controller='dataCtrl as dc' >
    
      <li ng-repeat="d in dc.data">
        {{d.name}}
        <a ng-controller="myPopoverCtrl"
           popover-template="myPopover.templateUrl" 
           popover-title="This is a popover" 
           popover-placement="right" 
           popover-is-open="myPopover.isOpen" 
           ng-click="myPopover.open(d)">
          pop
        </a>
      </li>
  </div>
  
  <script 
          type="text/ng-template"  
          id="myPopoverTemplate.html">
    <h2 ng-bind="myPopover.data"/>
    <button
        class="btn btn-success"
        ng-click="myPopover.close()">Close me!</button>
  
  </script>
  
</body>

Link to the working example

This is solution using another controller for the popover. this controller opens and closes the popover. you can also write the directive instead of controller. Its works fine if data is in repeat.