AngularJS : Differences among = & @ in directive scope?

50,475

Solution 1

This can be confusing but hopefully a simple example will clarify it. First, let's separate model bindings from behaviors.

Here is a fiddle that should help tie things together: http://jsfiddle.net/jeremylikness/3pvte/

And explained ... if your directive looks like this:

<my-directive target="foo"/> 

Then you have these possibilities for scope:

{ target : '=' } 

This will bind scope.target (directive) to $scope.foo (outer scope). This is because = is for two-way binding and when you don't specify anything, it automatically matches the name on the inner scope to the name of the attribute on the directive. Changes to scope.target will update $scope.foo.

{ bar : '=target' } 

This will bind scope.bar to $scope.foo. This is because again we specify two-way binding, but tell the directive that what is in the attribute "target" should appear on the inner scope as "bar". Changes to scope.bar will update $scope.foo.

{ target : '@' } 

This will set scope.target to "foo" because @ means "take it literally." Changes to scope.target won't propagate outside of your directive.

{ bar : '@target' } 

This will set scope.bar to "foo" because @ takes it's value from the target attribute. Changes to scope.bar won't propagate outside of your directive.

Now let's talk behaviors. Let's assume your outer scope has this:

$scope.foo = function(parm1, parm2) { console.log(parm1 + ": " + parm2); } 

There are several ways you can access this. If your HTML is:

<my-directive target='foo'>

Then

{ target : '=' } 

Will allow you to call scope.target(1,2) from your directive.

Same thing,

{ bar : '=target' }

Allows you to call scope.bar(1,2) from your directive.

The more common way is to establish this as a behavior. Technically, ampersand evaluates an expression in the context of the parent. That's important. So I could have:

<my-directive target="a+b" />

And if the parent scope has $scope.a = 1 and $scope.b = 2, then on my directive:

{ target: '&' } 

I can call scope.target() and the result will be 3. This is important - the binding is exposed as a function to the inner scope but the directive can bind to an expression.

A more common way to do this is:

<my-directive target="foo(val1,val2)"> 

Then you can use:

{ target: '&' }

And call from the directive:

scope.target({val1: 1, val2: 2}); 

This takes the object you passed, maps the properties to parameters in the evaluated expression and then calls the behavior, this case calling $scope.foo(1,2);

You could also do this:

<my-directive target="foo(1, val)"/>

This locks in the first parameter to the literal 1, and from the directive:

{ bar: '&target' }

Then:

scope.bar(5) 

Which would call $scope.foo(1,5);

Solution 2

Summary

  1. @attr binds to a matching DOM attribute's evaluated string value.
  2. =attr binds to a matching DOM attribute's scope property.
  3. &attr binds to a matching DOM attribute's scope function.
  4. @
  5. =
  6. &

We use the 4, 5, and 6 if the target DOM attribute's name matches the isolate scope proprty name. Here is a working fiddle of the following example.

Html

<div ng-app='isolate'>
     <h3>Outer Scope</h3>

    <input type="text" ng-model="myModel" />
    <p>msg: {{ msg }}</p>
     <h3>Inner Scope</h3>

    <div id="inner">
        <div my-directive at="{{ myModel }}" equals="myModel" ampersand="msg=msg+'click'"></div>
    </div>
</div>

Javascript

angular.module('isolate', [])
    .directive('myDirective', function () {
    return {
        template:
            '<label>@attr</label><input value="{{ myAt }}" />' +
            '<label>@</label><input value="{{ at }}" />' +
            '<label>=attr</label><input ng-model="myEquals" />' +
            '<label>=</label><input ng-model="equals" />' +
            '<label>&attr</label><input type="button" ng-click="myAmpersand()" value="Btn" />' +
            '<label>&</label><input type="button" ng-click="ampersand()" value="Btn" />',
        scope: {
            myAt: '@at',
            myEquals: '=equals',
            myAmpersand: '&ampersand',
            at: '@',
            equals: '=',
            ampersand: '&'
        }
    };
});
Share:
50,475

Related videos on Youtube

Shaun Luttin
Author by

Shaun Luttin

My professional work focuses on designing, testing, implementing/securing, and deploying distributed services. I kind be "that guy" too. Ship it!

Updated on March 07, 2020

Comments

  • Shaun Luttin
    Shaun Luttin over 4 years

    Creating an isolate scope inside a directive lets us map the outer scope to the inner scope. We have seen six different ways to map to attrbutes:

    1. =attr
    2. &attr
    3. @attr
    4. =
    5. &
    6. @

    What do each of these scope mapping options do?

    • Shaun Luttin
      Shaun Luttin over 10 years
      @karaxuna The other question does help. It doesn't include the $attr binding, though.
    • karaxuna
      karaxuna over 10 years
      = is same as =attr for example: scope: { smth: '=' } is same as scope: { smth: '=smth' }
    • Shaun Luttin
      Shaun Luttin over 10 years
    • rakslice
      rakslice about 8 years
      See also the official documentation for this, which is at docs.angularjs.org/api/ng/service/$compile#-scope- (the documentation for the 'Directive Definition Object' is hidden away inside the documentation for $compile)
  • Jeremy Likness
    Jeremy Likness over 10 years
    @ is a literal. & is a behavior, exposed as a function but can be set on the directive as an expression.
  • Jeremy Likness
    Jeremy Likness over 10 years
    Well didn't realize I can't "reverse a down vote" but I up-voted to fix the edit.
  • Shaun Luttin
    Shaun Luttin over 10 years
    Thank you for the clear explanation. Particularly helpful is the distinction between bindings and behaviors.
  • Buksy
    Buksy over 7 years
    Really nice example, but that scope.call(...) was little confusing to me as call is global javascript function and at first I thought u was trying to use the scope as function
  • alumi
    alumi about 6 years
    Why doesn't Angular have this in the document?