What is the difference between '@' and '=' in directive scope in AngularJS?

562,402

Solution 1

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. 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

Solution 2

There are a lot of great answers here, but I would like to offer my perspective on the differences between @, =, and & binding that proved useful for me.

All three bindings are ways of passing data from your parent scope to your directive's isolated scope through the element's attributes:

  1. @ binding is for passing strings. These strings support {{}} expressions for interpolated values. For example: . The interpolated expression is evaluated against directive's parent scope.

  2. = binding is for two-way model binding. The model in parent scope is linked to the model in the directive's isolated scope. Changes to one model affects the other, and vice versa.

  3. & binding is for passing a method into your directive's scope so that it can be called within your directive. The method is pre-bound to the directive's parent scope, and supports arguments. For example if the method is hello(name) in parent scope, then in order to execute the method from inside your directive, you must call $scope.hello({name:'world'})

I find that it's easier to remember these differences by referring to the scope bindings by a shorter description:

  • @ Attribute string binding
  • = Two-way model binding
  • & Callback method binding

The symbols also make it clearer as to what the scope variable represents inside of your directive's implementation:

  • @ string
  • = model
  • & method

In order of usefulness (for me anyways):

  1. =
  2. @
  3. &

Solution 3

The = means bi-directional binding, so a reference to a variable to the parent scope. This means, when you change the variable in the directive, it will be changed in the parent scope as well.

@ means the variable will be copied (cloned) into the directive.

As far as I know, <pane bi-title="{{title}}" title="{{title}}">{{text}}</pane> should work too. bi-title will receive the parent scope variable value, which can be changed in the directive.

If you need to change several variables in the parent scope, you could execute a function on the parent scope from within the directive (or pass data via a service).

Solution 4

If you would like to see more how this work with a live example. http://jsfiddle.net/juanmendez/k6chmnch/

var app = angular.module('app', []);
app.controller("myController", function ($scope) {
    $scope.title = "binding";
});
app.directive("jmFind", function () {
    return {
        replace: true,
        restrict: 'C',
        transclude: true,
        scope: {
            title1: "=",
            title2: "@"
        },
        template: "<div><p>{{title1}} {{title2}}</p></div>"
    };
});

Solution 5

@ get as string

  • This does not create any bindings whatsoever. You're simply getting the word you passed in as a string

= 2 way binding

  • changes made from the controller will be reflected in the reference held by the directive, and vice-versa

& This behaves a bit differently, because the scope gets a function that returns the object that was passed in. I'm assuming this was necessary to make it work. The fiddle should make this clear.

  • After calling this getter function, the resulting object behaves as follows:
    • if a function was passed: then the function is executed in the parent (controller) closure when called
    • if a non-function was passed in: simply get a local copy of the object that has no bindings


This fiddle should demonstrate how they work. Pay special attention to the scope functions with get... in the name to hopefully better understand what I mean about &

Share:
562,402
iwein
Author by

iwein

Check out these awesome teams Click here if you need help with AngularJS Click here if you need help with ReactJS Click here if you need a CTO Read more about me on LinkedIn

Updated on July 08, 2022

Comments

  • iwein
    iwein almost 2 years

    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/

  • iwein
    iwein over 11 years
    Yes, that part I get, see the fiddle in the question. But what about the parts that are unclear?
  • iwein
    iwein over 11 years
    the thing is that {{}} doesn't work with =. = isn't evaluated, but the string is taken as the property name as is. Thanks for the answer!
  • curot
    curot almost 11 years
    Huh, this is a really weird behavior, especially when not using interpolation and just trying to pass a string. Apparently the pull request has indeed been merged into the development builds and is in 1.1.5 and 1.2.0 RC builds. Good on them for fixing this very unintuitive behavior!
  • Den
    Den over 10 years
    Writing '@' or '=' is so much clearer then writing "eval-dom" or "parent-scope" or any other human-readable text. Good design decision.
  • Matt DeKrey
    Matt DeKrey about 10 years
    @ ('at') copies the value of the 'ATtribute'. = ('equals') is equivalent to saying the key equals your expression. This, at least, is how I keep them strait.
  • Jonathan Aquino
    Jonathan Aquino over 9 years
    Are you sure that = is only for parent-scope properties? Any expression seems to work - not only parent-scope properties.
  • Jonathan Aquino
    Jonathan Aquino over 9 years
    I don't think that = is just for variables in the parent scope. It works with any expression (e.g., 1+1).
  • Mark Rajcok
    Mark Rajcok over 9 years
    @JonathanAquino, well, since = sets up two-way data binding, the expression should be something assignable, and not just "any" expression.
  • Jonathan Aquino
    Jonathan Aquino over 9 years
    @MarkRajcok I don't think so - here is a CodePen showing the expression 1+1 passed into an = binding, and the result is 2: codepen.io/anon/pen/bDecJ
  • Mark Rajcok
    Mark Rajcok over 9 years
    @JonathanAquino, yes that works, but @ would be more appropriate -- with foo="{{1+1}}" -- because we don't need two-way data binding here. The point I tried to make in the comment above is that we should use = only when the directive needs two-way data binding. Use @ or & otherwise.
  • iwein
    iwein over 9 years
    There are several examples linked in the question and top answer. What does this add?
  • Tony Ennis
    Tony Ennis over 9 years
    @iwein, it adds clarity. If I could understand and assimilate full-featured examples, I would not need this site.
  • Tony Ennis
    Tony Ennis over 9 years
    juan, maybe fix your typos? 'transclude' is misspelled. better yet, remove it (and everything else, like 'replace') that does not contribute directly to the problem so your solution is even simpler and clearer. +1 for the example.
  • Juan Mendez
    Juan Mendez over 9 years
    thank you @AnikISlamAbhi for editing. I would like to contribute more and I am glad some find my samples helpful. That's the main purpose.
  • New Dev
    New Dev over 9 years
    Actually, "&" does support arguments (or, rather, locals) of the form: callback({foo: "some value"}), which could then be used <my-dir callback="doSomething(foo)">. Otherwise, good answer
  • Kevin
    Kevin about 9 years
    Should be accepted answer. Here is an concise article with the same information, but with added code examples: umur.io/…
  • iwein
    iwein about 9 years
    @JonathanAquino you're right that it evaluates expressions. imho this is actually weird and I wouldn't use it that way. It's this kind of clever tricks that make directive scopes so hard for me to understand in the first place.
  • Luc DUZAN
    Luc DUZAN about 9 years
    Am I the only one who think this answer is wrong ! '=' mean angular expect a javascript expression and will do a bidirectionnal mapping if a scope variable is passed. Whereas @ mean angular expect a String and that all. In fact, it's true that if you use @ in combinaison with {{}} you will clone the value of the variable. But it's not the definition of @ !
  • Mattygabe
    Mattygabe almost 9 years
    @LucDUZAN I would disagree: = does not mean Angular expects a JS expression, rather it expects an object that requires two way databinding. If the object changes in the directive, parent scope automatically receives updates to the object, and vice versa. @ evaluates what is passed to it from the parent scope to the directive, and the output is always a string for the directive. One way data binding that can only be viewed as a string. & is utilized for passing objects or methods to the directive, but is also a one way data binding (parent scope to directive).
  • Yugo Amaryl
    Yugo Amaryl almost 9 years
    What I gather is that "@" is evaluated before being passed to the directive, while "&" only creates a closure whose final evaluation will be triggered later inside the directive. Am I correct?
  • Niko Bellic
    Niko Bellic almost 9 years
    Awesome, but can someone explain why "$observe" is needed for "@" ??? EDIT: Nevermind, didn't see the link
  • Dmitri Zaitsev
    Dmitri Zaitsev over 8 years
    & is NOT "Callback method binding", it is Angular expression binding. A special but not the only example is the expression callback(argument). Which is still not the same as callback itself.
  • Dmitri Zaitsev
    Dmitri Zaitsev over 8 years
    & is neither "Behaviour binding" nor "Method binding", it is Angular expression binding.
  • TrueWill
    TrueWill over 8 years
    Oddly if I add a '@' property to scope I can access the current value in my link function just fine. There does not appear to be a need for $observe. Could you please clarify?
  • TrueWill
    TrueWill over 8 years
    Ah - apparently $observe() is for attributes that may contain curly-brace bindings. With static values it may not be necessary.
  • rbnzdave
    rbnzdave over 8 years
    While I loved how definitive the higher ranking answer was, I found this one made a more useful impact and after reading this one I understood the previous answer a lot more.
  • user3125823
    user3125823 over 8 years
    I agree with the above comment, this answer is more clear, definitive and useful to the question. It explains with enough detail that you can go and use the information.
  • iwein
    iwein about 8 years
    This answer is great and it most elegantly documents the scope features of $directive. However, Mark Rajcoks answer fits the original question more closely. There is not sufficient reason to unmark that one as the accepted answer. Apparently it is rewarding to read more than just the top answer on SO. All is well :)
  • The DIMM Reaper
    The DIMM Reaper about 8 years
    Would it be overreaching to ask for Angular 1.5's new "<" one-way binding type to be added to this already excellent answer?
  • The Red Pea
    The Red Pea over 7 years
    Using <ol> for an ordered list :)
  • Marc
    Marc over 6 years
    Very nice answer, finally got it :-) Thanks!
  • Sudarshan_SMD
    Sudarshan_SMD about 6 years
    Incomplete example. In your demonstration, you are changing only the bi-directional value. Your are not even trying to change value that has isolated scope. So, it does not properly demonstrated how scope works in directives.
  • Juan Mendez
    Juan Mendez about 6 years
    @Sudarshan_SMD What do you expect? That is from almost four years ago, move on and adapt to Angular 5!
  • Sudarshan_SMD
    Sudarshan_SMD about 6 years
    @JuanMendez Have already posted what I expected. My friend, sometimes you have to work on old projects. Your client won't always be willing to pay for the whole framework upgrade, when all they want is a small change in the software.
  • Sudhakar Dhayalan
    Sudhakar Dhayalan almost 3 years
    Simple and precise :)