为ui-bootstrap工具提示/弹出窗口动态设置attrs

时间:2015-05-12 20:41:38

标签: angularjs angular-ui-bootstrap

我尝试以编程方式切换工具提示(如此处所述:https://stackoverflow.com/a/23377441)并使其完全正常运行,除了一个问题。为了使它工作,我必须按如下方式对工具提示触发器和工具提示属性进行硬编码:

<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

1 个答案:

答案 0 :(得分:6)

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

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 属性)+ {{{ 1}}。这基本上评估为:$interpolate.endSymbol()。我使用$ interpolate服务的开始和结束符号,因为它只是使指令更加组件化,所以你可以在你可能有多个框架的其他项目上使用它,并为你的Angular表达式使用双花括号以外的东西。但这并不是必要的,你可以这样做:{{error.field1}}。在这种情况下,您不必将$ interpolate服务添加为依赖项。

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

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

最后,您可以利用以下事实:如果工具提示的文本值为空或未定义,则不会显示该工具提示(请参阅line 192)。这意味着您只需要查看错误对象而不是与工具提示相关的错误上的单个属性。确保将'{{'+errorObj+'.'+inputName+'}}'上的等于运算符设置为 true ,以便在任何对象的属性发生更改时触发它:

$watch

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

TL; DR:

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

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