Хороший способ динамически открывать / закрывать всплывающее окно (или всплывающую подсказку) с помощью angular на основе выражения?

У меня есть форма, подключенная к angular, и я использую ее для проверки. Я могу отображать сообщения об ошибках, используя следующие директивы ng-show:

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

.. где f - это форма, а t3 происходит от настраиваемой директивы формы, которая определяет, была ли попытка отправки, и содержит функции для проверки допустимости полей.

Я пытаюсь вместо этого отображать сообщения проверки внутри всплывающего окна. Либо родное всплывающее окно bootstrap, либо всплывающее окно из UI Bootstrap, я загрузил оба. Я также могу рассмотреть AngularStrap, если это проще сделать с помощью этой библиотеки.

То, с чем я сейчас борюсь, - это природа всплывающих окон в целом - они автоматически отображаются на основе пользовательских событий, таких как щелчок, ввод мыши, размытие и т. Д. Я хочу показать и скрыть всплывающие окна на основе того же функции в атрибутах ng-show выше. Чтобы, когда выражение возвращает false, скрыть его, а когда оно вернет true, показать его.

Я знаю, что у bootstrap для этого есть .popover ('show'), но я не должен говорить angular ничего о dom, поэтому я не уверен, как я получу доступ к $ (element) .popover (), если делая это в настраиваемой функции контроллера формы. Я что-то упускаю?

Обновить

Решение, упомянутое в повторном голосовании, по-прежнему показывает только всплывающее окно на mouseenter. Я хочу заставить его отображать, как будто делаю $('#popover_id').popover('show').


person danludwig    schedule 05.01.2014    source источник
comment
возможный дубликат Включить всплывающую подсказку angular -ui для пользовательских событий   -  person Stewie    schedule 06.01.2014
comment
@Stewie спасибо за ссылку.   -  person danludwig    schedule 06.01.2014
comment
@Stewie, это решение по-прежнему отображает всплывающее окно только при наведении мыши на элемент. Я хочу заставить его отображать, как будто делаю $('#popover_id').popover('show').   -  person danludwig    schedule 06.01.2014
comment
Это правда. Я вижу, что для этого есть открытая проблема с github, и она требует пиара.   -  person Stewie    schedule 06.01.2014
comment
@Stewie, не могли бы вы поделиться ссылкой на проблему gh? Я бы хотел посмотреть.   -  person danludwig    schedule 06.01.2014
comment
github.com/angular-ui/bootstrap/issues/590   -  person Stewie    schedule 06.01.2014


Ответы (6)


Вы также можете создавать свои собственные расширенные триггеры. Это применимо как к всплывающей подсказке, так и к всплывающему окну.

Сначала расширьте триггеры всплывающей подсказки следующим образом:

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

Затем определите триггер в теге HTML следующим образом:

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

И теперь вы можете вызывать скрыть и показать из JavaScript, это шоу за 3 секунды.

$("#RegisterHelp").trigger('show');
//Close the info again
$timeout(function () {
    $("#RegisterHelp").trigger('hide');
}, 3000);
person Kim Ras    schedule 30.04.2014
comment
Можете привести скрипку в качестве примера? Мои всплывающие окна не отображаются, когда я расширяю триггеры. - person Betty St; 21.05.2015
comment
Очень просто реализовать. Спасибо - person Sal; 09.06.2015
comment
Я этого не понимаю: { show: hide } - что это делает? - person chovy; 03.08.2015
comment
@chovy, этот бит регистрирует новое отображение событий, которое будет отображать / скрывать всплывающую подсказку / всплывающее окно. Если вы посмотрите на исходный код, то увидите triggerMap, который по умолчанию ставит mouseenter с mouseleave, click с click и focus с blur. Ключ в этом объекте - это событие, которое нужно прослушивать, чтобы показать всплывающее окно / всплывающую подсказку, а значение - это событие, которое нужно прослушивать, чтобы скрыть всплывающее окно / всплывающую подсказку. - person kevin; 17.10.2015

Оказывается, не очень сложно украсить всплывающую подсказку ui-bootstrap или всплывающее окно специальной директивой. Это написано на машинописном тексте, но части, содержащие javascript, должны быть очевидны. Этот единственный фрагмент кода украшает всплывающую подсказку или всплывающее окно:

'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;
    }
}

С помощью этого единственного фрагмента кода вы можете настроить программное скрытие и отображение всплывающей подсказки или всплывающего окна следующим образом:

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

Использование:

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

or

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

Поэтому мы повторно используем все остальное, что поставляется с всплывающей подсказкой или всплывающим окном ui-bootstrap, и реализуем только атрибут -toggle. Декоративная директива наблюдает за этим атрибутом и запускает настраиваемые события для отображения или скрытия, которые затем обрабатываются поставщиком всплывающей подсказки ui-bootstrap.

Обновление:

Поскольку этот ответ, похоже, помогает другим, вот код, написанный как javascript (приведенный выше машинописный текст более или менее компилируется в этот 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;
}
person danludwig    schedule 06.01.2014
comment
Спасибо за это, очень элегантное решение, которое лишь незначительно взламывает личные данные angular -ui. - person Matt Greer; 04.03.2014
comment
Похоже на хорошее решение, хотелось бы увидеть код в реальном JavaScript :) - person Petr Peller; 13.10.2014
comment
Предоставляется javascript @PetrPeller. - person danludwig; 18.11.2014
comment
Во-первых, мне очень нравится это решение! У меня только одна проблема, мой всплывающий экран не выключается. Правильно ли я понимаю, что "formName.fieldName.$error.required" является логическим в модели? Странно то, что всплывающее окно появляется, когда логическое значение изменяется на true, но, похоже, оно не переключается, когда оно снова изменяется на false. - person Benjamin Hammer Nørgaard; 26.02.2015
comment
При использовании шаблона popover это не работает. Это тоже не вызывает ошибки. Любые идеи? - person Matthias Max; 24.06.2015
comment
Да, вам нужно немного переписать его, чтобы он работал с popover-template, поскольку теперь он связывает директиву с именем, которое вы передаете функции. - person Aron Lorincz; 24.09.2015

Для ui.bootstrap 0.13.4 и новее:

Был введен новый параметр (popover-is-open) для управления всплывающими окнами в официальном репозитории ui.bootstrap. Вот как вы его используете в последней версии:

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

Для ui.bootstrap 0.13.3 и старше:

Я только что опубликовал небольшую директиву, которая добавляет больше контроля над всплывающими окнами на GitHub:
https://github.com/Elijen/angular-popover-toggle.

Вы можете использовать переменную области видимости, чтобы показать / скрыть всплывающее окно, используя директиву popover-toggle="variable" следующим образом:

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

Вот демонстрация Plunkr:
http://plnkr.co/edit/QeQqqEJAu1dCuDtSvomD?p=preview

person Petr Peller    schedule 12.07.2015
comment
это то, что должно быть в ядре. Текущая реализация написана для angular, но, похоже, предназначена для работы по старой школе. angular использует часы, jquery (и прочее) управляется событиями. - person Sam; 05.01.2016
comment
+1, в большинстве случаев это кажется предполагаемым разработчиком библиотеки и наиболее естественным способом «вручную» открывать и закрывать всплывающее окно. - person chinnychinchin; 14.09.2016

Мой подход:

  • Отслеживайте состояние всплывающего окна в модели
  • Измените это состояние для каждого элемента с помощью соответствующих директив.

Идея в том, чтобы оставить манипуляции с DOM директивам.

Я собрал скрипку, которая, надеюсь, лучше объясняет, но вы найдете гораздо более сложные решения в UI Bootstrap, о которых вы упомянули.

jsfiddle

Разметка:

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

}
person Ehimen    schedule 05.01.2014
comment
Я думаю, что в этом подходе есть некоторые достоинства, и он хорошо работает, но использование компонента для этого намного чище, гибче и пригодно для повторного использования. - person Michael Yagudaev; 31.01.2014

Для других, приходящих сюда, начиная с версии 0.13.4, мы добавили возможность программно открывать и закрывать всплывающие окна с помощью атрибута *-is-open как во всплывающих подсказках, так и во всплывающих окнах в библиотеке Angular UI Bootstrap. Таким образом, больше нет причин для развертывания собственного кода / решения.

person icfantv    schedule 23.10.2015

Из ответа Майкла Страмела, но с полным решением angularJS:

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

Теперь добавьте эту директиву:

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

            })
        }
    }
}])

Затем в вашей разметке:

<span tooltip-trigger="show" tooltip="Login or register here" nt-trigger-if="{'show':{{ (errorConidtion) }}, 'hide':{{ !(errorConidtion) }} }"></span>
person Shawn Dotey    schedule 18.09.2015