AngularJS Directives, Using Isolated Scope with Attributes

Directives in AngularJS are very powerful, but it takes some time to understand what processes lie behind them.
While creating directives, AngularJS allows you to create an isolated scope with some custom bindings to the parent scope. These bindings are specified by the attribute defined in HTML and the definition of the scope property in the directive definition object.

There are 3 types of binding options which are defined as prefixes in the scope property. The prefix is followed by the attribute name of HTML element. These types are as follows

  1. Text Binding (Prefix: @)
  2. One-way Binding (Prefix: &)
  3. Two-way Binding (Prefix: =)

Let's see how these are defined in directives, and I'll go into details one by one.

JS:

angular.module("myApp",[])  
  .directive("myDirective", function () {
    return {
      restrict: "A",
      scope: {
        text: "@myText",
        twoWayBind: "=myTwoWayBind",
        oneWayBind: "&myOneWayBind"
      }
    };
  }).controller("myController", function ($scope) {
    $scope.foo = {name: "Umur"};
    $scope.bar = "qwe";
});

HTML:

<div ng-controller="myController">  
  <div my-directive
    my-text="hello {{ bar }}"
    my-two-way-bind="foo"
    my-one-way-bind="bar">
  </div>
</div>  

1. Text Binding

Text bindings are prefixed with @, and they are always strings. Whatever you write as attribute value, it will be parsed and returned as strings. Which means anything inside curly braces {{ }}, will reflect the value.

So, if we were to define $scope.username = "Umur", and define the attribute like <my-directive my-attribute="hello {{ userName }}" />, the value in the directive scope is going to be hello Umur. This value would be updated in each digest cycle.

2. One-way Binding

One-way bindings are prefixed with & and they can be of any type. They are going be defined as getter functions in the directive scope. See it with example:

Controller:

/* more code */
$scope.someObject = { name:'Umur', id:1 };
/* more code */

HTML:

<my-directive my-attribute="someObject" />

Directive:

{
  scope: {myValue: "&myAttribute"},
  link: function (scope, iElm, iAttrs) {
    var x = scope.myValue();
    // x == {name:"Umur", id:1}
  }
}

Since they are getter functions, they are read-only, any changes to the value will not propagated to higher scopes.

3. Two-way Bindings

Two-way bindings are prefixed by = and can be of any type. These work like actual bindings, any changes to a bound value will be reflected in everywhere.

Let's see it by example:

Controller:

/* more code */
$scope.someObject = { name:'Umur', id:1 };
/* more code */

HTML:

<my-directive my-attribute="someObject" />  

Directive:

{
  scope: {myValue: "=myAtrribute" },
  link: function (scope, iElm, iAttrs) {
    var x = scope.myValue.name;
    // x == "Umur";
    scope.myValue.name = "Kieslowski";
    // if we go to parent scope (controller's scope, in this example)
    // $scope.someObject.name == "Kieslowski";
    // would be true
  }
}

Summary with Code

JS:

angular.module("myApp", [])  
  .directive("myDirective", function () {
    return {
      restrict: "A",
      scope: {
        text: "@myText",
        twoWayBind: "=myTwoWayBind",
        oneWayBind: "&myOneWayBind"
      }
    };
}).controller("myController", function ($scope) {
  $scope.foo = {name: "Umur"};
  $scope.bar = "qwe";
});

HTML:

<div ng-controller="myController">  
  <div my-directive 
       my-text="hello {{ bar }}" 
       my-two-way-bind="foo" 
       my-one-way-bind="bar">
  </div>
</div>  

Results:

/* Directive scope */

in: $scope.text  
out: "hello qwe"  
// this would automatically update the changes of value in digest
// this is always string as dom attributes values are always strings

in: $scope.twoWayBind  
out: {name:"Umur"}  
// this would automatically update the changes of value in digest
// changes in this will be reflected in parent scope

// in directive's scope
in: $scope.twoWayBind.name = "John"

//in parent scope
in: $scope.foo.name  
out: "John"

in: $scope.oneWayBind() // notice the function call, this binding is read only  
out: "qwe"  
// any changes here will not reflect in parent, as this only a getter .

If you like this post and would like to know when I posted something new in here, follow me on Twitter @umurkontaci.