Create Hoverable popover using angular-ui-bootstrap

后端 未结 11 1028
渐次进展
渐次进展 2020-12-09 10:45

I have the following code for creating a popover in my template file:



        
相关标签:
11条回答
  • 2020-12-09 10:47

    I have solved it in a very cleaned way and thought to share it:

    .popover is being created not as a child of the uib-popover so the idea is to wrap uib-popover with a parent and to control show&hide on hovering the parent.

    .popover and uib-popover are children of this parent so just left to set popover-trigger=none and you have what you are wishing for.

    I created a plunk example:

    <span ng-init="popoverOpened=false" ng-mouseover="popoverOpened=true" ng-mouseleave="popoverOpened=false">
        <button class="btn btn-default" uib-popover-html="htmlPopover" 
                popover-trigger="none" popover-placement="bottom-left" popover-is-open="popoverOpened" >
           <span>hover me</span>
        </button>
    </span>
    

    enjoy.

    0 讨论(0)
  • 2020-12-09 10:47

    I don't know if this is relevant to the OP anymore, but I've had the same problem and fortunately I managed to solve it.

    Undefined error

    First thing first, the undefined error you are getting might be (at least in my case) because you are using the development version of ui-bootstrap. In my case I got this error when trying to bind element.popover. After adding the minified version of the library the error went away.

    Keep the popover open when hovering over it

    To do this I have created a custom directive that makes use of the popover from the ui-bootstrap library.

    Directive

    app.directive('hoverPopover', function ($compile, $templateCache, $timeout, $rootScope) {
    var getTemplate = function (contentType) {
        return $templateCache.get('popoverTemplate.html');
    };
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            var content = getTemplate();
            $rootScope.insidePopover = false;
            $(element).popover({
                content: content,
                placement: 'top',
                html: true
            });
            $(element).bind('mouseenter', function (e) {
                $timeout(function () {
                    if (!$rootScope.insidePopover) {
                        $(element).popover('show');
                        scope.attachEvents(element);
                    }
                }, 200);
            });
            $(element).bind('mouseleave', function (e) {
                $timeout(function () {
                    if (!$rootScope.insidePopover)
                        $(element).popover('hide');
                }, 400);
            });
        },
        controller: function ($scope, $element) {
            $scope.attachEvents = function (element) {
                $('.popover').on('mouseenter', function () {
                    $rootScope.insidePopover = true;
                });
                $('.popover').on('mouseleave', function () {
                    $rootScope.insidePopover = false;
                    $(element).popover('hide');
                });
            }
        }
    };
    });
    

    This directive also accepts a custom template for the popover, so you are not limited to just title and some text in it. You can create your own html template and feed it to the control.

    Usage

    <a href="#" hover-popover>Click here</a>
    

    Hopes this helps someone else in the future :)

    Edit

    As requested, here is a Fiddle link. It lacks the styling, but it should demonstrate the way it works.

    0 讨论(0)
  • 2020-12-09 10:47

    You have to put the trigger in single quotes, because, reasons:

    <button uib-popover="I appeared on mouse enter!" popover-trigger="'mouseenter'" type="button" class="btn btn-default">Mouseenter</button>
    
    0 讨论(0)
  • 2020-12-09 10:48

    I needed to do this as well. I have a checkbox in a table cell that can have 3 possible states: Enabled, Disabled, or Special case. The UI spec I have asked for a popover over the box that shows either of those statuses, or for the special case a sentence with a link.

    I tried several of these solutions and one of them worked for me, and they all added extra code. After some playing around, I determined I could just add the "popover-popup-close-delay" attribute with a dynamic value. So this works for me:

    <td uib-popover-html="getPopoverTxt()" popover-popup-close-delay="{{ele.isspecial ? 2000 : 300}}" popover-popup-delay="300" popover-append-to-body="true" popover-placement="top" popover-trigger="mouseenter">
        <input id="issynced{{ele.id}}" name="isChecked" type="checkbox" data-ng-checked="ele.ischecked" data-ng-model="ele.ischecked">
        <label for="issynced{{ele.id}}"></label>
    </td>
    

    Some context: My table is looping over an array of data objects, so ele is a single object. The getPopoverTxt() is just a simple method in my controller that returns one of the 3 labels I want to show ("Enabled", "Disabled", or "Special Text with HTML"). Its not necessary here, but the takeaway is to get the HTML to work, you have to wrap the string value in $sce.trustAsHtml(), like:

    var specialText = $sce.trustAsHtml('Text with a link to <a href="/help" target="_blank">contact support</a>');
    

    The rest is all the usual popover and form input settings we normally use. The "popover-popup-close-delay" is the key.

    0 讨论(0)
  • 2020-12-09 10:51

    demo:

    https://jsbin.com/fuwarekeza/1/edit?html,output

    directive:

    myAppModule.directive('popoverHoverable', ['$timeout', '$document', function ($timeout, $document) {
        return {
            restrict: 'A',
            scope: {
                popoverHoverable: '=',
                popoverIsOpen: '='
            },
            link: function(scope, element, attrs) {
                scope.insidePopover = false;
    
                scope.$watch('insidePopover', function (insidePopover) {
                    togglePopover(insidePopover);
                })
    
                scope.$watch('popoverIsOpen', function (popoverIsOpen) {
                    scope.insidePopover = popoverIsOpen;
                })
    
                function togglePopover (isInsidePopover) {
                    $timeout.cancel(togglePopover.$timer);
                    togglePopover.$timer = $timeout(function () {
                        if (isInsidePopover) {
                            showPopover();
                        } else {
                            hidePopover();
                        }
                    }, 100)
                }
    
                function showPopover () {
                    if (scope.popoverIsOpen) {
                        return;
                    }
    
                    $(element[0]).click();
                }
    
                function hidePopover () {
                    scope.popoverIsOpen = false;
                }
    
                $(document).bind('mouseover', function (e) {
                    var target = e.target;
                    if (inside(target)) {
                        scope.insidePopover = true;
                        scope.$digest();
                    }
                })
    
                $(document).bind('mouseout', function (e) {
                    var target = e.target;
                    if (inside(target)) {
                        scope.insidePopover = false;
                        scope.$digest();
                    }
                })
    
                scope.$on('$destroy', function () {
                    $(document).unbind('mouseenter');
                    $(document).unbind('mouseout');
                })
    
                function inside (target) {
                    return insideTrigger(target) || insidePopover(target);
                }
    
                function insideTrigger (target) {
                    return element[0].contains(target);
                }
    
                function insidePopover (target) {
                    var isIn = false;
                    var popovers = $('.popover-inner');
                    for (var i = 0, len = popovers.length; i < len; i++) {
                        if (popovers[i].contains(target)) {
                            isIn = true;
                            break;
                        }
                    }
                    return isIn;
                }
            }
        }
    }]);
    

    html:

    <span class="icon-globe visibility" 
          id="visibilityFor{{post.metaData.assetId}}" 
          popover="{{post.visibilityListStr}}" 
          popover-is-open="{{post.$open}}"
          popover-trigger="click" 
          popover-hoverable="true"
          visibility>
    </span>
    
    0 讨论(0)
  • 2020-12-09 10:57

    I think Cosmin has the hoverable popover right, but it does seem to be using the Twitter Bootstrap popover method. The idea is to have this hoverable popover implemented only with AngularJS and one of the Bootstrap wrappers for AngularJS, which are UI Bootstrap or AngularStrap.

    So I have put together an implementation which uses only AngularStrap:

    myApp.directive('hoverablePopover', function ($rootScope, $timeout, $popover) {
        return {
            restrict: "A",
            link: function (scope, element, attrs) {
    
                element.bind('mouseenter', function (e) {
                    $timeout(function () {
                        if (!scope.insidePopover) {
    
                            scope.popover.show();
                            scope.attachEventsToPopoverContent();
                        }
                    }, 200);
                });
    
                element.bind('mouseout', function (e) {
                    $timeout(function () {
                        if (!scope.insidePopover) {
    
                            scope.popover.hide();
                        }
                    }, 400);
                });
    
            },
            controller: function ($scope, $element, $attrs) {
    
                //The $attrs will server as the options to the $popover.
                //We also need to pass the scope so that scope expressions are supported in the  popover attributes
                //like title and content.
                $attrs.scope = $scope;
                var popover = $popover($element, $attrs);
                $scope.popover = popover;
                $scope.insidePopover = false;
    
                $scope.attachEventsToPopoverContent = function () {
    
                    $($scope.popover.$element).on('mouseenter', function () {
    
                        $scope.insidePopover = true;
    
                    });
                    $($scope.popover.$element).on('mouseleave', function () {
    
                        $scope.insidePopover = false;
                        $scope.popover.hide();
    
                    });
                };
            }
        };
    });
    

    When you have a popover element, you need to take into account that you have the element that triggers the popover and you also have the element with the actual popover content.

    The idea is to keep the popover open when you mouse over the element with the actual popover content. In the case of my directive, the link function takes care of the element that triggers the popover and attaches the mouseenter/mouseout event handlers.

    The controller takes care of setting the scope and the popover itself via the AngularStrap $popover service. The controller adds the popover object returned by the AngularStrap service on the scope so that it is available in the link function. It also adds a method attachEventsToPopoverContent, which attaches the mouseenter/mouseout events to the element with the popover content.

    The usage of this directive is like this:

      <a title="Popover Title" data-placement="left" data-trigger="manual" data-content="{{someScopeObject}}" content-template="idOfTemplateInTemplateCache" hoverablePopover="">
    
    0 讨论(0)
提交回复
热议问题