为 ui-bootstrap 工具提示/弹出框动态设置属性

Setting attrs dynamically for ui-bootstrap tooltip / popover

我正在尝试以编程方式切换工具提示(如此处所述:)并使其功能齐全,但有一个问题除外。为了让它工作,我必须将 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) 指令的 plunker:http://plnkr.co/edit/mP0JD8KHt4ZR3n0vF46e

你可以这样做,但你必须改变你的方法中的一些东西。

Plunker Demo

指令

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 服务来执行此操作,但您只需为元素执行一次。

此外,您需要将工具提示文本(赋予 tooltip 属性的值)绑定到表达式。我通过传入 $interpolate.startSymbol() 的串联值 + 您要显示的范围值(在演示中它是字段 x 属性错误对象)+ $interpolate.endSymbol()。这基本上评估为:{{error.field1}}。我使用 $interpolate 服务开始和结束符号,因为它只会使指令更加组件化,因此您可以在可能有多个框架的其他项目中使用它,并且为您的 Angular 使用双花括号以外的东西表达式。但这不是必需的,您可以改为:'{{'+errorObj+'.'+inputName+'}}'。在这种情况下,您不必将 $interpolate 服务添加为依赖项。

如您所见,为了使指令真正可重用,而不是对错误字段进行硬编码,我将赋予指令属性的值设置为将被监视的对象的名称,并使用输入名称作为对象的值 属性。

您需要记住的主要事情是,在编译之前,您必须从元素中删除错误工具提示属性,因为如果不这样做,您将陷入无限循环并崩溃!基本上,编译服务将获取指令所附加的元素,并使用指令添加的所有属性对其进行编译,如果您保留错误工具提示属性,它也会尝试重新编译该指令。

最后,您可以利用以下事实:如果其文本值为空或未定义,工具提示将不会显示(请参阅 line 192)。这意味着您只需要在与工具提示相关的错误上观察错误对象而不是个体 属性。确保将 $watch 上的相等运算符设置为 true,以便在对象的任何属性发生更改时触发:

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

在演示中,您可以看到更改错误对象的效果。如果单击“设置错误”按钮,工具提示将显示第一个和第二个输入。单击更改错误值,将显示第一个和第三个输入的工具提示。

长话短说:

通过将值设置为将包含所有错误的对象的名称,将指令添加到您的标记中。确保为该字段提供一个名称属性,该属性对应于将包含该输入错误的对象中的 属性 键名称,例如:

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