Skip to main content
Ben Nadel at cf.Objective() 2010 (Minneapolis, MN) with: Seth Bienek
Ben Nadel at cf.Objective() 2010 (Minneapolis, MN) with: Seth Bienek

You Cannot Link Attribute Interpolation Multiple Times In AngularJS

By
Published in

Once you have a compiled directive, in AngularJS, you can either link it to the current scope; or, you can clone the compiled template and link each new clone to a different scope. But, what if we didn't want to clone the template? What if we just wanted to move it around in the DOM (Document Object Model)? Could we simply re-link it to different scopes? From what I see, the answer depends on the content of the template. It appears that attribute interpolation does not play nicely with re-linking.

Run this demo in my JavaScript Demos project on GitHub.

To test this approach, I've created an ngRepeat list that doesn't have any actual content. But, it does have a "bnFriend" directive. This directive will pre-compile a template and then attempt to inject and link said template into each ngRepeat as the user mouses into the list element. Since I am not cloning the template, each .append() call will implicitly remove the template from its former parent node.

This demo uses its own version of the AngularJS library that I have augmented with some logging. Specifically, I have added log statements to the Text Interpolation and the Attribute Interpolation directives so that I can see when they fire and what state they are in at link time.

When I mouse into the first list item, we get the following console output:

Attribute Interpolation Prelinking on title
>>> Original value: {{ friend.name }}
>>> Current value: {{ friend.name }}
Attribute Interpolation Prelinking on ngSrc
>>> Original value: ./avatar-{{ friend.avatarID }}.jpg
>>> Current value: ./avatar-{{ friend.avatarID }}.jpg
Text Interpolation Linking on {{ friend.name }}
Text Interpolation Linking on {{ friend.nickname }}

As you can see, the attribute interpolation sees the {{}} syntax markers.

Now, when I mouse out of the first list item and into the second list item, we get the following console output when the compiled template is re-linked to the new scope:

$destroy called on: 009 with 4 watchers.
Attribute Interpolation Prelinking on title
>>> Original value: {{ friend.name }}
>>> Current value: Sarah
>>> CAUTION: No interpolation requirements found in attribute, Sarah
Attribute Interpolation Prelinking on ngSrc
>>> Original value: ./avatar-{{ friend.avatarID }}.jpg
>>> Current value: ./avatar-1.jpg
>>> CAUTION: No interpolation requirements found in attribute, ./avatar-1.jpg
Text Interpolation Linking on {{ friend.name }}
Text Interpolation Linking on {{ friend.nickname }}

Notice that this time the "current value" of the attribute doesn't contain the {{}} interpolation markers. As such, the second pre-linking phase doesn't see any need for interpolation and therefore does not bind any watchers for attribute interpolation. This is why the attribute interpolation works on the first list item, but not on any of the subsequent list items.

Notice, however, that the Text Interpolation continues to work on each list item. This is because the text interpolation link function does not get recomputed for each linking phase. As such, it uses the original template value on each subsequent linking action.

If you look at the source code for the attribute interpolation directive, there is a note on why they recompute the linking function:

// we need to interpolate again, in case the attribute value has been updated
// (e.g. by another directive's compile function)

I think this is a byproduct of the order in which attribute interpolation is applied in the context of other directives. But, the AngularJS source code for compiling HTML is, what one might call, "complicated". As such, I can't make any confident statements about it. But, from my testing, it seems re-linking a compiled HTML template in different contexts is going to be a dangerous move. Probably, it's just better to clone the template and then link it.

Want to use code from this post? Check out the license.

Reader Comments

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel