95

My AngularJS template contains some custom HTML syntax like:

<su-label tooltip="{{field.su_documentation}}">{{field.su_name}}</su-label>

I created a directive to process it:

.directive('suLabel', function() {
  return {
    restrict: 'E',
    replace: true,
    transclude: true,
    scope: {
      title: '@tooltip'
    },
    template: '<label><a href="#" rel="tooltip" title="{{title}}" data-placement="right" ng-transclude></a></label>',
    link: function(scope, element, attrs) {
      if (attrs.tooltip) {
        element.addClass('tooltip-title');
      }
    },
  }
})

Everything works fine, at the exception of the attrs.tooltip expression, which always returns undefined, even though the tooltip attribute is visible from Google Chrome's JavaScript console when doing a console.log(attrs).

Any suggestion?

UPDATE: A solution was offered by Artem. It consisted in doing this:

link: function(scope, element, attrs) {
  attrs.$observe('tooltip', function(value) {
    if (value) {
      element.addClass('tooltip-title');
    }
  });
}

AngularJS + stackoverflow = bliss

Sangwin Gawande
  • 7,658
  • 8
  • 48
  • 66
Ismael Ghalimi
  • 3,515
  • 2
  • 22
  • 25
  • This [answer](http://stackoverflow.com/questions/12008580/a-ternary-in-angular-templates-angularjs/12151492#12151492) to another question explains how to properly express a ternary in AngularJS. – Ismael Ghalimi Aug 28 '12 at 04:08
  • So very this: "AngularJS + stackoverflow = bliss" – twip Feb 07 '15 at 01:27

2 Answers2

83

See section Attributes from documentation on directives.

observing interpolated attributes: Use $observe to observe the value changes of attributes that contain interpolation (e.g. src="{{bar}}"). Not only is this very efficient but it's also the only way to easily get the actual value because during the linking phase the interpolation hasn't been evaluated yet and so the value is at this time set to undefined.

Artem Andreev
  • 19,942
  • 5
  • 43
  • 42
25

Although using '@' is more appropriate than using '=' for your particular scenario, sometimes I use '=' so that I don't have to remember to use attrs.$observe():

<su-label tooltip="field.su_documentation">{{field.su_name}}</su-label>

Directive:

myApp.directive('suLabel', function() {
    return {
        restrict: 'E',
        replace: true,
        transclude: true,
        scope: {
            title: '=tooltip'
        },
        template: '<label><a href="#" rel="tooltip" title="{{title}}" data-placement="right" ng-transclude></a></label>',
        link: function(scope, element, attrs) {
            if (scope.title) {
                element.addClass('tooltip-title');
            }
        },
    }
});

Fiddle.

With '=' we get two-way databinding, so care must be taken to ensure scope.title is not accidentally modified in the directive. The advantage is that during the linking phase, the local scope property (scope.title) is defined.

Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • Hey Mark, what is your opinion on using these solutions, is there a specific guideline for using observe on the link attrs against using the two-way-databinding ? I think it looks cleaner to use the two-way databinding but I wonder if there is a reason to not use it? – Jeroen Jan 06 '15 at 12:15
  • @Jeroen, I posted a longer discussion of using `@` vs `=` [here](http://stackoverflow.com/a/14063373/215945). – Mark Rajcok Jan 07 '15 at 17:11