94

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

2
  • This answer to another question explains how to properly express a ternary in AngularJS. Commented Aug 28, 2012 at 4:08
  • So very this: "AngularJS + stackoverflow = bliss" Commented Feb 7, 2015 at 1:27

2 Answers 2

82

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.

Sign up to request clarification or add additional context in comments.

1 Comment

24

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.

2 Comments

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, I posted a longer discussion of using @ vs = here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.