web-dev-qa-db-fra.com

Un bon moyen d’ouvrir / fermer dynamiquement un popover (ou une info-bulle) en utilisant angular, en fonction de 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ù f est le formulaire et t3 provient d'une directive personnalisée du formulaire, qui détecte si une soumission a été tentée et contient des fonctions permettant de vérifier la validité des champs.

Ce que je cherche à faire est d’afficher des messages de validation dans un popover. Popover natif de bootstrap, ou le popover de I Bootstrap , je les ai tous les deux chargés. Je peux aussi envisager AngularStrap s'il est plus facile de le faire en utilisant cette bibliothèque.

Ce qui me pose problème à l’heure actuelle, c’est la nature des popovers en général - ils affichent automatiquement l’affichage en fonction d’événements tels que les clics, le mouseenter, le flou, etc. fonctions dans les attributs ng-show ci-dessus. Ainsi, lorsque l'expression renvoie false, masquez-la et, lorsqu'elle renvoie true, affichez-la.

Je sais que bootstrap a le .popover ('show') pour cela, mais je ne suis pas supposé dire à angular rien à propos du dom, alors je ' Je ne sais pas comment j'aurais accès à $ (élément) .popover () si je le faisais avec une fonction de contrôleur de formulaire personnalisée.

Mettre à 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
danludwig

En fait, 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 code unique décore 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 morceau de code unique, vous pouvez configurer le masquage et l’affichage programmés d’une info-bulle ou d’un popover de la manière suivante:

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);

Usage:

<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>

Nous réutilisons donc tout ce qui vient avec l'infobulle ui-bootstrap ou popover, et implémentons uniquement le -toggle attribut. 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 se compile plus ou moins en 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
danludwig

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

Tout d'abord, étendez les déclencheurs de l'info-bulle comme suit:

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

Définissez ensuite le déclencheur sur la balise HTML comme suit:

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

Et maintenant, vous pouvez appeler masquer et afficher à partir de JavaScript. Il s'agit d'un spectacle en 3 secondes.

$("#RegisterHelp").trigger('show');
//Close the info again
$timeout(function () {
    $("#RegisterHelp").trigger('hide');
}, 3000);
29
Kim Ras

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 jeu officiel ui.bootstrap repo. 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 ancien:

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

Vous pouvez utiliser une variable d’étendue pour afficher/masquer la popover à l’aide de popover-toggle="variable" directive comme ceci:

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

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

16
Petr Peller

À partir de la version 0.13.4, nous avons ajouté la possibilité d'ouvrir et de fermer par programmation des popovers via le *-is-open attribut sur les info-bulles et les popovers dans la bibliothèque Angular UI Bootstrap. Il n’ya donc plus de raison de lancer votre propre code/solution) .

4
icfantv

Mon approche:

  • Suivre l'état de la popover dans le modèle
  • Changez cet état par élément en utilisant les directives appropriées.

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

J'ai monté un violon qui, je l'espère, donne une meilleure explication, mais vous trouverez des solutions beaucoup plus sophistiquées dans l'interface utilisateur Bootstrap dont vous avez parlé.

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
Ehimen

D'après 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
Shawn Dotey