Ember.js and Related Technologies
I've noticed recently that there's been some confusion about {{bind}}
and {{bindAttr}} in Ember. Both of these helpers serve the same
purpose, to bind properties into your templates, so why do we have two
versions? To understand this a better, we need to know a bit about
how {{bind}} works behind the scenes.
NOTE: {{bind}} is the default helper. If you do {{propName}} it's
the same as doing {{bind propName}}. I'll refer to this as {{bind}}
throught this post.
If you look at the source of your page with the WebKit Inspector or
Firebug, you'll notice something unusual going on with {{bind}}: it's
wrapped in <script> tags! So why do we do this? Well when a bound
property updates, we need to know where in the DOM to make the changes.
In the SproutCore 2 days, we tried wrapping things in <span> tags.
This normally worked fine, except it was a pain to style. People didn't like us
messing with their DOM. Furthermore, if you tried to put a binding
inside of certain tags like <table> or <select> then things really went
downhill.
Metamorph solves these problems by using <script> tags to mark the
content so we know where to update. <script> tags are almost entirely
transparent to the DOM1. They also are valid nearly
everywhere in the document, including inside of <table> and <select>
tags. For more information on Metamorph, see its README
Imagine the results of using {{bind}} within a tag. If you try the
following:
<a href="{{url}}">The Emberist</a>
You'll get something along the lines of:
<a href="
<script id="metamorph-0-start" type="text/x-placeholder"></script>
http://emberist.com
<script id="metamorph-0-end" type="text/x-placeholder">
">The Emberist</a>
This is clearly not valid DOM and it won't work.
This happens because Handlebars isn't context aware. It doesn't know
that we are inside of a tag and attempts to output a <script> tag even
though it isn't valid.
{{bindAttr}} works by outputting a data-bindattr attribute in
addition to the attribute that you specified. In the previous code, we
would change to:
<a {{bindAttr href="url"}}>The Emberist</a>
This instead will output something along the lines of:
<a href="http://emberist.com" data-bindattr-1="1">The Emberist</a>
Ember keeps an internal reference to the binding and, when the property
changes, it looks for the tag with data-bindattr-1 and updates the
specified attribute.
Now you have a better understanding of not just what you need to do to make things work, but why they actually work.
1. The only known problem in using <script> tags is that they do
affect the CSS :first-child, :last-child, and :nth-child
selectors. One solution is to use :first-of-type, :last-of-type, and
:nth-of-type respectively, though this is not supported in all
browsers.