Setting dynamic parameters for ui-bootstrap tooltip / popover

I have been trying to programmatically toggle tooltips (like this one here: https://stackoverflow.com/a/1130910/ ) and got it fully working except for one problem. In order for it to work, I must have the tooltip-trigger and tooltip attributes coded like this:

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

      

In my work directive, I can change the tooltip attributes and call the tooltip, but if I try to leave these two attributes and try to set them dynamically, ui-bootstrap does not pick them up and no tooltip pops up.

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

      

I would rather not hardcode these attributes in the html, since I need to dynamically set these attributes and get the ui-bootstrap to pick them up?

Here is a plunker that has a valid directive (field1) and non working (field2): http://plnkr.co/edit/mP0JD8KHt4ZR3n0vF46e

+3


source to share


2 answers


You can do this, but you have to change a couple of things in your approach.

Demo version of the plunger

Directive

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

    }
  };
});

      

Very long detailed explanation:

Ok, so the top: @Travis is correct in that you can't just type in the attributes after the fact. The tooltip attributes you place on the element are the directives themselves, so the tooltip should be compiled when added. This is not a problem, you can use the $ compile service to do this, but you only need to do it once per item.

Also, you need to bind the tooltip text (the value assigned to the tooltip attribute) to the expression. I do this by passing in the concatenated value $interpolate.startSymbol()

+ the scope value you want to display (in the demo this is the fieldx property of the errors object) + $interpolate.endSymbol()

. This is mainly estimated to be approximately as follows: {{error.field1}}

. I use the start and end characters of the $ interpolation service because it just makes the directive more component-based, so you can use it in other projects where you can have multiple frameworks and use something other than double curly braces for your Angular expressions ... It is not necessary although you could do: '{{'+errorObj+'.'+inputName+'}}'

. In this case, you don't need to add the $ interpolation service as a dependency.

As you can see, in order to make the directive truly reusable and not hardcode the error field, I set the value given to the directive attribute to the name of the object to be viewed and use the value of the input name as a property of the object.



The main thing to remember is that you need to remove the hint attribute from the element before compiling , because if you don't, you end up in an infinite loop and crash! Basically, the compilation service is going to take the element that the directive is attached to and compile it with all the attributes added by your directive, if you leave the error hint attribute, it will try to recompile that directive as well.

Finally, you can take advantage of the fact that the tooltip will not display if its text value is empty or undefined (see line 192 ). This means that you only need to look at the errors object, not the individual error property associated with the tooltip. Make sure you set the equality operator $watch

to true , so that it works if any of the object's properties have changed:

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

      

In the demo, you can see the effect of changing the error object. If you click the Set Errors button, a tooltip will be displayed for both the first and second inputs. Click Change Error Values ​​and tooltips will be displayed for the first and third inputs.

TL; DR:

Add a directive to your markup, setting it to a value that will contain all errors. Be sure to include a name attribute for the field that matches the name of the property key in the object that will contain errors for that input, such as:

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

      

+5


source


Have you tried using scope. $ apply after attrs installed?



scope.$apply(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');
});`

      

-1


source







All Articles