Question
I've read the AngularJS documentation on the topic carefully, and then fiddled around with a directive. Here's the fiddle.
And here are some relevant snippets:
-
From the HTML :
<pane bi-title="title" title="{{title}}">{{text}}</pane>
-
From the pane directive:
scope: { biTitle: '=', title: '@', bar: '=' },
There are several things I don't get:
- Why do I have to use
"{{title}}"
with'@'
and"title"
with'='
? - Can I also access the parent scope directly, without decorating my element with an attribute?
- The documentation says "Often it's desirable to pass data from the isolated scope via expression and to the parent scope" , but that seems to work fine with bidirectional binding too. Why would the expression route be better?
I found another fiddle that shows the expression solution too: http://jsfiddle.net/maxisam/QrCXh/
Answer
Why do I have to use "{{title}}" with ' @ ' and "title" with ' = '?
@ binds a local/directive scope property to the evaluated value of the
DOM attribute. If you use title=title1
or title="title1"
, the value of
DOM attribute "title" is simply the string title1
. If you use
title="{{title}}"
, the value of the DOM attribute "title" is the
interpolated value of {{title}}
, hence the string will be whatever parent
scope property "title" is currently set to. Since attribute values are always
strings, you will always end up with a string value for this property in the
directive's scope when using @.
= binds a local/directive scope property to a parent scope property.
So with = , you use the parent model/scope property name as the value of
the DOM attribute. You can't use {{}}
s with =.
With @, you can do things like title="{{title}} and then some"
-- {{title}}
is interpolated, then the string "and them some" is concatenated with it. The
final concatenated string is what the local/directive scope property gets.
(You can't do this with = , only @.)
With @ , you will need to use attr.$observe('title', function(value) { ... })
if you need to use the value in your link(ing) function. E.g.,
if(scope.title == "...")
won't work like you expect. Note that this means
you can only access this attribute
[asynchronously](https://github.com/angular/angular.js/wiki/Understanding-
Directives). You don't need to use $observe() if you are only using the value
in a template. E.g., template: '<div>{{title}}</div>'
.
With = , you don't need to use $observe.
Can I also access the parent scope directly, without decorating my element with an attribute?
Yes, but only if you don't use an isolate scope. Remove this line from your directive
scope: { ... }
and then your directive will not create a new scope. It will use the parent scope. You can then access all of the parent scope properties directly.
The documentation says "Often it's desirable to pass data from the isolated scope via an expression and to the parent scope", but that seems to work fine with bidirectional binding too. Why would the expression route be better?
Yes, bidirectional binding allows the local/directive scope and the parent scope to share data. "Expression binding" allows the directive to call an expression (or function) defined by a DOM attribute -- and you can also pass data as arguments to the expression or function. So, if you don't need to share data with the parent -- you just want to call a function defined in the parent scope -- you can use the & syntax.
See also
- Lukas's isolated scope blog post (covers @, =, &)
- dnc253's explanation of @ and =
- my blog-like answer about scopes -- the directives section (way at the bottom, just before the Summary section) has a picture of an isolate scope and its parent scope -- the directive scope uses @ for one property and = for another
- What is the difference between & vs @ and = in angularJS