Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angularjs directive with isolated scope needs clarification

Edit:

There is a functional example in this plunker:

http://plnkr.co/edit/GFQGP0q3o9RjLAlRANPS?p=preview

Outer scope has $scope.name = 'Donald'

All directives are declared as:

<directive-name binding="name">

This is a multi-part question. Im trying to gain a better understanding of isolated scopes that has a watch or a binding to an outer scope variable.

With a non-isolated scope directive everything works as it should:

  // [WORKS]
  .directive('noScopeWithWatch', function(){
    return {
      restrict: 'E',
      link: function(scope, lElement, attrs) {
        scope.$watch(attrs.binding, function(name){
          lElement.text('Hello ' + name);
        });
      }
    };
  })
  // returns Hello Donald

The confusing part is when I try to isolate the scope and keep the binding. So what Im asking for is a clarification on why some of the following examples work, while and others do not.

If I just add the scope isolation with a 'normal' binding it fails:

  // 1. [FAILS]
  .directive('scopeWithWatch', function(){
    return {
      restrict: 'E',
      link: function(scope, lElement, attrs) {
        scope.$watch(attrs.binding, function(name){
          lElement.text('Hello ' + name);
        });
      },
      scope: {                                   // new content
        binding: '='                             // new content
      }                                          // new content
    };
  })
  // returns Hello undefined

However, using the binding variable in the watch as a string makes it work:

  // 2. [WORKS]
  .directive('scopeWithWatchString', function(){
    return {
      restrict: 'E',
      link: function(scope, lElement, attrs) {
        scope.$watch('binding', function(b){     // new content
          lElement.text('Hello ' + b);
        });
      },
      scope: {
        binding: '=' 
      }
    };
  })
  // returns Hello Donald

While using the binding variable as an object fails misribly:

  // 3. [FAILS]
  .directive('scopeWithWatchObject', function(){
    return {
      restrict: 'E',
      link: function(scope, lElement, attrs) {
        scope.$watch(binding, function(b){       // new content
          lElement.text('Hello ' + b);
        });
      },
      scope: {
        binding: '='
      }
    };
  })
  // Does not work at all
  // Console output - ReferenceError: binding is not defined

Trying to reference the binding variable insinde the isolated scope does not work either, but at least does not cause an exception :

  // 4. [FAILS]
  .directive('scopeWithWatchScopeObject', function(){
    return {
      restrict: 'E',
      link: function(scope, lElement, attrs) {
        scope.$watch(scope.binding, function(b){  // new content
          lElement.text('Hello ' + b);
        });
      },
      scope: {
        binding: '='
      }
    };
  })
  // returns Hello undefined

It turns out that using the binding variable in mustaches in a template works:

  // 5. [WORKS]
  .directive('scopeWithTemplate', function(){
    return {
      restrict: 'E',
      template: 'Hello {{binding}}',     // new content and linker removed
      scope: {
        binding: '='
      }
    };
  })
  // returns Hello Donald

But trying to use them as mustaches in the linker does not.

  // 6. [FAILS]
  .directive('scopeWithWatchStringUsingMustashes', function(){
    return {
      restrict: 'E',     
      link: function(scope, lElement, attrs) {        // new content
        scope.$watch('binding', function(){           // new content
          lElement.text('Hello {{binding}}');         // new content  
        });                                           // new content
      },                                              // new content
      scope: {
        binding: '='
      }
    };
  })
  // returns Hello {{binding}}

Here is the plunker:

http://plnkr.co/edit/GFQGP0q3o9RjLAlRANPS?p=preview (Im currently at version 78, please fork if you want to use it in your answer.)

Could someone please explain to me why some examples work while others do not.

like image 698
Presidenten Avatar asked Jun 18 '26 04:06

Presidenten


1 Answers

There is a simple answer to this, which applies to all the examples here. The Angular documentation on $compile explains this, but it is easy to misunderstand. The entire purpose of an isolated scope is to create a scope that is consumed ONLY by the directive that declares it. In order to do so, a new variable is created which stores the value as an alias of the parent scope.

There are 3 major definition types: @, =, &

@ or @attr - bind a local scope property to the value of DOM attribute. The result is always a string since DOM attributes are strings.

= or =attr - set up bi-directional binding between a local scope property and the parent scope property of name defined via the value of the attr attribute.

& or &attr - provides a way to execute an expression in the context of the parent scope.

The only difference between @ and = is bi-directional support. The = definition will still return a string result.

So, what you have, in order, is:

  1. Isolated scope does not provide access to the attrs collection, it only has access to it's own isolated scope items. This will not work, as there is no attrs object in the scope.
  2. Works as intended, binding='name' is matched to binding: '=' and 'binding' is accessable as an alias in the isolated scope.
  3. Fails, because attrs are always strings, they are not javascript objects.
  4. Fails,

    The 'isolate' scope differs from normal scope in that it does not prototypically inherit from the parent scope. This is useful when creating reusable components, which should not accidentally read or modify data in the parent scope.

  5. Works, for the same reason as 2. The bindings are properly matched. Templates are handled by $compile automatically.
  6. Fails, because the lElement.text is simply that, text. using an expression within a text assignment requires an additional manual compile step before the text is sent to the dom, else the {{}} expression is treated as plain text.
like image 199
Claies Avatar answered Jun 20 '26 18:06

Claies