Динамическая настройка attrs для всплывающей подсказки/поповера ui-bootstrap

Я пытаюсь программно переключать всплывающие подсказки (как указано здесь: https://stackoverflow.com/a/23377441) и получил это полностью рабочий, за исключением одной проблемы. Чтобы это работало, у меня должны быть жестко закодированы атрибуты tooltip-trigger и tooltip следующим образом:

<input type="text" tooltip-trigger="show" tooltip="" field1>

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

HTML

<input type="text" field2>

js

myApp.directive('field2', function($timeout) {
    return {
        scope: true,
        restrict: 'A',
        link: function(scope, element, attrs) {

            scope.$watch('errors', function() {
                var id = "field2";
                if (scope.errors[id]) {
                    $timeout(function(){
                      // these attrs dont take effect...
                        attrs.$set('tooltip-trigger', 'show');
                        attrs.$set('tooltip-placement', 'top');

                        attrs.$set('tooltip', scope.errors[id]);
                        element.triggerHandler('show');
                    });
                    element.bind("click", function(e){
                      element.triggerHandler('hide');
                    });
                }
            });
        },
    };
});

Я бы предпочел не жестко кодировать эти атрибуты в html, так как мне настроить эти атрибуты динамически и заставить ui-bootstrap их подобрать?

Вот плункер с рабочей (field1) и нерабочей (field2) директивами: http://plnkr.co/edit/mP0JD8KHt4ZR3n0vF46e


person redgoose    schedule 12.05.2015    source источник
comment
Однако применяемые настройки attrs.$set('tooltip-trigger', 'show'); не приводят к тому, что обработчик событий всплывающей подсказки прикрепляется к элементу. Angular анализирует html на ранней стадии, но простое изменение атрибута не заставит его внезапно заметить и связать событие. Я не уверен в лучшем способе динамического применения события. Вот несколько связанный ответ: stackoverflow.com/questions/20939754/   -  person Travis J    schedule 13.05.2015
comment
Тогда, я полагаю, это объясняет. Этот связанный ответ был моей основной помощью (я связался с ним в своем вопросе).   -  person redgoose    schedule 13.05.2015


Ответы (1)


Вы можете сделать это, но вы должны изменить пару вещей в своем подходе.

Демонстрация Plunker

Директива

app.directive("errorTooltip", function($compile, $interpolate, $timeout) {
  return {
    scope: true,
    link: function($scope, $element, $attrs) {
      var errorObj = $attrs.errorTooltip;
      var inputName = $attrs.name;
      var startSym = $interpolate.startSymbol();
      var endSym = $interpolate.endSymbol();
      var content = startSym+errorObj+'.'+inputName+endSym;
      $element.attr('tooltip-trigger', 'show');
      $element.attr('tooltip-placement', 'top');
      $element.attr('tooltip', content);
      $element.removeAttr('error-tooltip');
      $compile($element)($scope);

      $scope.$watch(errorObj, function() {
        $timeout(function(){
          $element.triggerHandler('show');
        });
      }, true);

      $element.on('click', function(e){
        $element.triggerHandler('hide');
      });

    }
  };
});

Супер длинное подробное объяснение:

Итак, сверху: @Travis прав в том, что вы не можете просто вводить атрибуты постфактум. Атрибуты всплывающей подсказки, которые вы размещаете в элементе, сами по себе являются директивами, поэтому всплывающую подсказку необходимо скомпилировать, когда она добавлена. Это не проблема, вы можете использовать сервис $compile для этого, но вам нужно сделать это только один раз для элемента.

Кроме того, вам необходимо привязать текст всплывающей подсказки (значение, присвоенное атрибуту всплывающей подсказки) к выражению. Я делаю это, передавая конкатенированное значение $interpolate.startSymbol() + значение области видимости, которое вы хотите отобразить (в демонстрации это свойство fieldx объекта ошибок) + $interpolate.endSymbol(). В основном это оценивается примерно так: {{error.field1}}. Я использую начальный и конечный символы службы $interpolate, потому что это просто делает директиву более компонентной, поэтому вы можете использовать ее в других проектах, где у вас может быть несколько фреймворков и вы используете что-то кроме двойных фигурных скобок для ваших выражений Angular. Это не обязательно, и вместо этого вы можете сделать: '{{'+errorObj+'.'+inputName+'}}'. В этом случае вам не нужно добавлять службу $interpolate в качестве зависимости.

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

Главное, что вам нужно помнить, это то, что перед компиляцией вы должны удалить атрибут error-tooltip из элемента, потому что, если вы этого не сделаете, вы попадете в бесконечный цикл и сильно упадете. ! По сути, служба компиляции возьмет элемент, к которому прикреплена директива, и скомпилирует его со всеми атрибутами, добавленными вашей директивой. Если вы оставите атрибут error-tooltip, она также попытается перекомпилировать эту директиву.

Наконец, вы можете воспользоваться тем фактом, что всплывающая подсказка не будет отображаться, если ее текстовое значение пусто или не определено (см. строка 192). Это означает, что вам нужно следить только за объектом ошибок, а не за отдельным свойством ошибки, связанной с всплывающей подсказкой. Убедитесь, что вы установили для оператора равенства в $watch значение true, чтобы он срабатывал при изменении любого из свойств объекта:

  $scope.$watch('errors', function() {
    $timeout(function(){
      $element.triggerHandler('show');
    });
  }, true); //<--equality operator

В демо вы можете увидеть эффект от изменения объекта ошибок. Если вы нажмете кнопку «Установить ошибки», всплывающая подсказка будет отображаться как для первого, так и для второго ввода. Нажмите «Изменить значения ошибок», и появится всплывающая подсказка для первого и третьего входных данных.

TL;DR:

Добавьте директиву в разметку, задав в качестве значения имя объекта, который будет содержать все ошибки. Обязательно задайте для поля атрибут имени, соответствующий имени ключа свойства в объекте, который будет содержать ошибки для этого ввода, например:

<input class="form-control" ng-model="demo.field1" name="field1" error-tooltip="errors" />
person jme11    schedule 21.05.2015