Bon moyen d'ouvrir / fermer dynamiquement un popover (ou une info-bulle) en utilisant angular, basé sur l'expression?

J'ai un formulaire qui est câblé dans angular, l'utilisant pour la validation. Je suis capable d'Afficher des messages d'erreur en utilisant les directives ng-show comme ceci:

<span ng-show="t3.f.needsAttention(f.fieldName)" ng-cloak>
    <span ng-show="f.fieldName.$error.required && !f.fieldName.$viewValue">
        This field is required.
    </span>
</span>

.. où {[1] } est le formulaire, et {[2] } provient d'une directive personnalisée sur le formulaire qui détecte si une soumission a été tentée, et contient des fonctions pour vérifier la validité des champs.

Ce que j'essaie d'accomplir est d'afficher le(s) message (s) de validation dans un popover à la place. Soit le popover natif de bootstrap, soit le popover de UI Bootstrap , j'ai tous les deux chargés. Je peux aussi considérer AngularStrap s'il est plus facile de le faire en utilisant cette lib.

Ce que je suis aux prises avec en ce moment est la nature des popovers en général-ils autodisplay basé sur des événements utilisateur comme click, mouseenter, blur, etc. Ce que je veux faire est de montrer et cacher le popover(s) basé sur les mêmes fonctions dans les attributs ng-show ci-dessus. De sorte que, lorsque l'expression renvoie false cacher, et quand elle retourne true, montrer il.

Je sais que bootstrap a le .popover ('show') pour cela, mais je ne suis pas censé dire quoi que ce soit à angular sur le dom, donc je ne suis pas sûr de savoir comment j'aurais accès à $(element).popover () si vous le faites dans une fonction de contrôleur de formulaire personnalisé. Ai-je raté quelque chose?

Mise à Jour

La solution mentionnée dans le vote en double ne montre toujours que le popover sur mouseenter. Je veux le forcer à s'afficher, comme si je faisais $('#popover_id').popover('show').

28
demandé sur danludwig 2014-01-06 02:01:05

6 réponses

Vous pouvez également créer vos propres déclencheurs étendus. Cela s'appliquera à la fois à L'infobulle et au Popover.

D'abord étendre les déclencheurs D'info-bulle comme suit:

// define additional triggers on Tooltip and Popover
app.config(['$tooltipProvider', function($tooltipProvider){
    $tooltipProvider.setTriggers({
        'show': 'hide'
    });
}]);

Ensuite, définissez le déclencheur sur la balise HTML comme ceci:

<div id="RegisterHelp" popover-trigger="show" popover-placement="left" popover="{{ 'Login or register here'}}">

Et maintenant vous pouvez appeler hide and show à partir de JavaScript, c'est un spectacle en 3 secondes.

$("#RegisterHelp").trigger('show');
//Close the info again
$timeout(function () {
    $("#RegisterHelp").trigger('hide');
}, 3000);
29
répondu Kim Ras 2015-09-18 22:23:53

Il s'avère qu'il n'est pas très difficile de décorer l'info-bulle ui-bootstrap ou le popover avec une directive personnalisée. Ceci est écrit en typescript, mais les parties javascript devraient être évidentes. Ce seul morceau de code fonctionne pour décorer une info-bulle ou un popover:

'use strict';

module App.Directives.TooltipToggle {

    export interface DirectiveSettings {
        directiveName: string;
        directive: any[];
        directiveConfig?: any[];
    }

    export function directiveSettings(tooltipOrPopover = 'tooltip'): DirectiveSettings {

        var directiveName = tooltipOrPopover;

        // events to handle show & hide of the tooltip or popover
        var showEvent = 'show-' + directiveName;
        var hideEvent = 'hide-' + directiveName;

        // set up custom triggers
        var directiveConfig = ['$tooltipProvider', ($tooltipProvider: ng.ui.bootstrap.ITooltipProvider): void => {
            var trigger = {};
            trigger[showEvent] = hideEvent;
            $tooltipProvider.setTriggers(trigger);
        }];

        var directiveFactory = (): any[] => {
            return ['$timeout', ($timeout: ng.ITimeoutService): ng.IDirective => {
                var d: ng.IDirective = {
                    name: directiveName,
                    restrict: 'A',
                    link: (scope: ng.IScope, element: JQuery, attr: ng.IAttributes) => {

                        if (angular.isUndefined(attr[directiveName + 'Toggle'])) return;

                        // set the trigger to the custom show trigger
                        attr[directiveName + 'Trigger'] = showEvent;

                        // redraw the popover when responsive UI moves its source
                        var redrawPromise: ng.IPromise<void>;
                        $(window).on('resize', (): void => {
                            if (redrawPromise) $timeout.cancel(redrawPromise);
                            redrawPromise = $timeout((): void => {
                                if (!scope['tt_isOpen']) return;
                                element.triggerHandler(hideEvent);
                                element.triggerHandler(showEvent);

                            }, 100);
                        });

                        scope.$watch(attr[directiveName + 'Toggle'], (value: boolean): void => {
                            if (value && !scope['tt_isOpen']) {
                                // tooltip provider will call scope.$apply, so need to get out of this digest cycle first
                                $timeout((): void => {
                                    element.triggerHandler(showEvent);
                                });
                            }
                            else if (!value && scope['tt_isOpen']) {
                                $timeout((): void => {
                                    element.triggerHandler(hideEvent);
                                });
                            }
                        });
                    }
                };
                return d;
            }];
        };

        var directive = directiveFactory();

        var directiveSettings: DirectiveSettings = {
            directiveName: directiveName,
            directive: directive,
            directiveConfig: directiveConfig,
        };

        return directiveSettings;
    }
}

Avec ce seul morceau de code, vous pouvez configurer le masquage et l'affichage programmatiques d'une info-bulle ou d'une popover comme ceci:

var tooltipToggle = App.Directives.TooltipToggle.directiveSettings();
var popoverToggle = App.Directives.TooltipToggle.directiveSettings('popover');
var myModule = angular.module('my-mod', ['ui.bootstrap.popover', 'ui.bootstrap.tpls'])
    .directive(tooltipToggle.directiveName, tooltipToggle.directive)
        .config(tooltipToggle.directiveConfig)
    .directive(popoverToggle.directiveName, popoverToggle.directive)
        .config(popoverToggle.directiveConfig);

Utilisation:

<span tooltip="This field is required."
    tooltip-toggle="formName.fieldName.$error.required"
    tooltip-animation="false" tooltip-placement="right"></span>

Ou

<span popover="This field is required."
    popover-toggle="formName.fieldName.$error.required"
    popover-animation="false" popover-placement="right"></span>

Donc nous sommes réutiliser tout le reste fourni avec l'info-bulle ui-bootstrap ou popover, et implémenter uniquement l'attribut -toggle. La directive décorative surveille cet attribut et déclenche des événements personnalisés à afficher ou à masquer, qui sont ensuite gérés par le fournisseur d'info-bulle ui-bootstrap.

Mise à Jour:

Puisque cette réponse semble aider les autres, voici le code écrit en javascript (le typescript ci-dessus compile plus ou moins à ce javascript):

'use strict';

function directiveSettings(tooltipOrPopover) {

    if (typeof tooltipOrPopover === "undefined") {
        tooltipOrPopover = 'tooltip';
    }

    var directiveName = tooltipOrPopover;

    // events to handle show & hide of the tooltip or popover
    var showEvent = 'show-' + directiveName;
    var hideEvent = 'hide-' + directiveName;

    // set up custom triggers
    var directiveConfig = ['$tooltipProvider', function ($tooltipProvider) {
        var trigger = {};
        trigger[showEvent] = hideEvent;
        $tooltipProvider.setTriggers(trigger);
    }];

    var directiveFactory = function() {
        return ['$timeout', function($timeout) {
            var d = {
                name: directiveName,
                restrict: 'A',
                link: function(scope, element, attr) {
                    if (angular.isUndefined(attr[directiveName + 'Toggle']))
                        return;

                    // set the trigger to the custom show trigger
                    attr[directiveName + 'Trigger'] = showEvent;

                    // redraw the popover when responsive UI moves its source
                    var redrawPromise;
                    $(window).on('resize', function() {
                        if (redrawPromise) $timeout.cancel(redrawPromise);
                        redrawPromise = $timeout(function() {
                            if (!scope['tt_isOpen']) return;
                            element.triggerHandler(hideEvent);
                            element.triggerHandler(showEvent);

                        }, 100);
                    });

                    scope.$watch(attr[directiveName + 'Toggle'], function(value) {
                        if (value && !scope['tt_isOpen']) {
                            // tooltip provider will call scope.$apply, so need to get out of this digest cycle first
                            $timeout(function() {
                                element.triggerHandler(showEvent);
                            });
                        }
                        else if (!value && scope['tt_isOpen']) {
                            $timeout(function() {
                                element.triggerHandler(hideEvent);
                            });
                        }
                    });
                }
            };
            return d;
        }];
    };

    var directive = directiveFactory();

    var directiveSettings = {
        directiveName: directiveName,
        directive: directive,
        directiveConfig: directiveConfig,
    };

    return directiveSettings;
}
20
répondu danludwig 2014-11-18 15:30:06

Pour ui.bootstrap 0.13.4 et plus récent:

Un nouveau paramètre (popover-is-open) a été introduit pour contrôler les popovers dans le repo officiel ui.bootstrap. Voici comment vous l'utilisez dans la dernière version:

<a uib-popover="Hello world!" popover-is-open="isOpen" ng-click="isOpen = !isOpen">
   Click me to show the popover!
</a>

Pour ui.bootstrap 0.13.3 et plus:

Je viens de publier une petite directive qui ajoute plus de contrôle sur popovers sur GitHub:
https://github.com/Elijen/angular-popover-toggle

Vous pouvez utiliser une variable de portée pour afficher / masquer le popover en utilisant la directive popover-toggle="variable" comme ce:

<span popover="Hello world!" popover-toggle="isOpen">
   Popover here
</span>

Voici une démo Plunkr:
http://plnkr.co/edit/QeQqqEJAu1dCuDtSvomD?p=preview

16
répondu Petr Peller 2016-01-24 12:31:49

Mon approche:

  • suivre l'état du popover dans le modèle
  • Modifiez cet état par élément en utilisant les directives appropriées.

L'idée étant de laisser la manipulation DOM aux directives.

J'ai mis en place un violon qui, j'espère, donne une meilleure explication, mais vous trouverez des solutions beaucoup plus sophistiquées dans UI Bootstrap que vous avez mentionné.

Jsfiddle

Balisage:

<div ng-repeat="element in elements" class="element">

    <!-- Only want to show a popup if the element has an error and is being hovered -->
    <div class="popover" ng-show="element.hovered && element.error" ng-style>Popover</div>

    <div class="popoverable" ng-mouseEnter="popoverShow(element)" ng-mouseLeave="popoverHide(element)">
        {{ element.name }}
    </div>

</div>

JS:

function DemoCtrl($scope)
{

    $scope.elements = [
        {name: 'Element1 (Error)', error: true, hovered: false},
        {name: 'Element2 (no error)', error: false, hovered: false},
        {name: 'Element3 (Error)', error: true, hovered: false},
        {name: 'Element4 (no error)', error: false, hovered: false},
        {name: 'Element5 (Error)', error: true, hovered: false},
    ];

    $scope.popoverShow = function(element)
    {
        element.hovered = true;
    }

    $scope.popoverHide = function(element)
    {
        element.hovered = false
    }

}
4
répondu Ehimen 2014-01-05 22:45:46

De la réponse de Michael Stramel, mais avec une solution angularJS complète:

// define additional triggers on Tooltip and Popover
app.config(['$tooltipProvider', function($tooltipProvider){
    $tooltipProvider.setTriggers({
       'show': 'hide'
    });
}])

Ajoutez maintenant cette directive:

app.directive('ntTriggerIf', ['$timeout',
function ($timeout) {
    /*
    Intended use:
        <div nt-trigger-if={ 'triggerName':{{someCodition === SomeValue}},'anotherTriggerName':{{someOtherCodition === someOtherValue}} } ></div>
    */
    return {

        restrict: 'A',
        link: function (scope, element, attrs) {

            attrs.$observe('ntTriggerIf', function (val) {
                try {

                    var ob_options = JSON.parse(attrs.ntTriggerIf.split("'").join('"') || "");
                }
                catch (e) {
                    return
                }

                $timeout(function () {
                    for (var st_name in ob_options) {
                        var condition = ob_options[st_name];
                        if (condition) {
                            element.trigger(st_name);
                        }
                    }
                })

            })
        }
    }
}])

Puis dans votre balisage:

<span tooltip-trigger="show" tooltip="Login or register here" nt-trigger-if="{'show':{{ (errorConidtion) }}, 'hide':{{ !(errorConidtion) }} }"></span>
3
répondu Shawn Dotey 2015-09-18 21:36:30

Pour les autres qui viennent ici, à partir de la version 0.13.4, nous avons ajouté la possibilité d'ouvrir et de fermer par programmation popovers via l'attribut *-is-open sur les infobulles et popovers dans la bibliothèque Angular UI Bootstrap. Ainsi, il n'y a plus aucune raison d'avoir à rouler votre propre code/solution.

3
répondu icfantv 2015-10-23 15:28:30