具有孤立范围的Angularjs指令需要澄清

时间:2014-03-10 07:20:21

标签: javascript angularjs angularjs-directive angularjs-scope

编辑:

这个plunker有一个功能性的例子:

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

外部范围有$ scope.name ='唐纳德'

所有指令都声明为:

<directive-name binding="name">

这是一个多部分的问题。我试图更好地理解具有监视或绑定到外部范围变量的孤立范围。

使用非隔离范围指令,一切正常:

  // [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

令人困惑的部分是当我试图隔离范围并保持绑定时。所以我要求的是澄清为什么下面的一些例子有效,而有些则没有。

如果我只是用“普通”绑定添加范围隔离,它就会失败:

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

但是,将watch中的绑定变量用作字符串可以使其工作:

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

虽然使用绑定变量作为对象失败但是:

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

尝试在孤立范围内引用绑定变量也不起作用,但至少不会导致异常:

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

事实证明,在模板中的胡须中使用绑定变量可以起作用:

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

但是尝试将它们用作链接器中的胡须并不会。

  // 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}}

以下是摘要:

http://plnkr.co/edit/GFQGP0q3o9RjLAlRANPS?p=preview (我现在的版本是78,如果您想在答案中使用它,请分叉。)

有人可以向我解释为什么有些例子有效,而其他例子没有。

2 个答案:

答案 0 :(得分:4)

这有一个简单的答案,适用于此处的所有示例。关于$compile的Angular文档解释了这一点,但很容易误解。隔离范围的整个目的是创建一个仅由声明它的指令消耗的范围。为此,将创建一个新变量,将该值存储为父作用域的别名。

有三种主要定义类型:@=&

  

@或@attr - 将本地范围属性绑定到DOM属性的值。结果总是一个字符串,因为DOM属性是字符串。

     

= or = attr - 在本地范围属性和通过attr属性的值定义的name的父范围属性之间设置双向绑定。

     

&安培;或&amp; attr - 提供了一种在父作用域的上下文中执行表达式的方法。

@=之间的唯一区别是双向支持。 =定义仍将返回字符串结果。

所以,按顺序,你拥有的是:

  1. 隔离范围不提供对attrs集合的访问,它只能访问它自己的隔离范围项。这不起作用,因为范围内没有attrs对象。
  2. 按预期工作,binding='name'binding: '='匹配,'binding'可作为隔离范围内的别名访问。
  3. 失败,因为attrs总是字符串,它们不是javascript对象。
  4. 失败,

      

    'isolate'范围与正常范围的不同之处在于它不是从父范围原型继承的。这在创建可重用组件时非常有用,这些组件不应该意外地读取或修改父范围中的数据。

  5.   
  6. 工作,原因与2.绑定正确匹配。模板由$compile自动处理。
  7.   
  8. 失败,因为lElement.text只是文本。在文本分配中使用表达式需要在将文本发送到dom之前进行额外的手动编译步骤,否则{{}}表达式将被视为纯文本。
  9.   

答案 1 :(得分:2)

让我试着解决这些问题

  1. 这不起作用,因为$ watch方法采用在范围上定义的表达式。 attrs.binding的值为"name",隔离范围内没有name属性。
  2. 因为watch位于范围的binding属性上而起作用。
  3. 这是不允许的。这应该是字符串。
  4. 这应该有效。不知道为什么这不起作用。
  5. binding在隔离范围内定义,并且可以正常工作
  6. 乳清你设置lElement.text('Hello {{binding}}');,它需要编译要评估的表达式的内容,因此这个表达式不起作用。
  7. 当你说

    <scope-with-watch binding="name"></scope-with-watch>并且执行隔离范围,绑定在父作用域上定义的name属性和隔离作用域上的binding属性之间。