【问题标题】:Angular Directive's template binding doesn't updateAngular Directive 的模板绑定不会更新
【发布时间】:2014-01-21 05:30:49
【问题描述】:

我在这里设置了一个指令http://jsfiddle.net/screenm0nkey/8Cw4z/3,它有两个绑定到同一个作用域属性,但由于某种原因,当模型更改时(在输入输入后),指令模板属性中的绑定不会更新。

<test>
    <h3>Inner {{count}}</h3>
    <input type="text" ng-model="count">
</test>

var App = angular.module('App', []);
App.directive('test', function() {
    return {
      restrict: 'E',
      replace: true,
      transclude: true,
      template: "<h1>Outer{{count}} <div ng-transclude></div></h1>",
      controller: function ($scope) {
        $scope.count = 1;
      }
    };
  });

但如果我在标记中移动输入位置,它会起作用并且两个绑定都会更新。

<input type="text" ng-model="count">
<test>
     <h3>Inner {{count}}</h3>
</test>

http://jsfiddle.net/screenm0nkey/dCvZk/3

谁能解释为什么包含绑定的输入的位置会影响绑定。我假设在摘要循环期间,无论标记的位置如何,两个绑定的观察者都会更新。

非常感谢

【问题讨论】:

    标签: angularjs angularjs-directive


    【解决方案1】:

    这里是a work around

    $scope.count更改为

    $scope.helper = {
        count: 1
    }
    

    并重构其余部分。

    Check this video out 解释一下。

    【讨论】:

    • 嗨乔纳森,这确实有效。我不想解决任何问题。我只是想知道为什么它不起作用。你能告诉我为什么它适用于范围内的嵌套属性,而不适用于直接在 $scope 上的属性吗?你也能告诉我为什么移动输入也有效。非常感谢
    • 你看视频了吗,它可能会回答你的问题。
    • 我回复时视频链接不存在,但我会看看。非常非常感谢:)
    【解决方案2】:

    这是一个范围界定问题。

    $scope.count = 1; 将属性 count 添加到 &lt;test&gt; 所在的范围内。我们称之为父范围。

    ng-transclude 创建一个新的作用域,我们称之为子作用域。在评估 &lt;h3&gt;Inner {{count}}&lt;/h3&gt; 时,子作用域没有属性 count,因此它是从父作用域读取的。

    &lt;input type="text" ng-model="count"&gt; 将输入值绑定到子作用域中的属性count。一旦你输入了一些东西,如果它还没有,它就会被创建。从此时起,&lt;h3&gt;Inner {{count}}&lt;/h3&gt; 从子作用域获取其值。

    Angular 中的作用域是简单的 JavaScript 对象,通过原型连接到它们的父对象。所以在你输入一些东西之前,子范围看起来像

    {
      prototype: { // = parent scope
         count: 1
      }
    }
    

    当您将值更改为 5 时,范围看起来像

    {
      count: 5,
      prototype: { // = parent scope
         count: 1
      }
    }
    

    因为数据绑定的作用类似于scope.count = 5

    【讨论】:

      【解决方案3】:

      长话短说 - 正如其他人所说,这是一个范围问题。使用“ng-transclude”指令会创建一个新范围。当从旧范围创建新范围时, 将可以在新范围中访问(因此是第一次替换),但之后只会更新旧/新范围之间共享的对象。这就是为什么使用对象会起作用,但使用值不会。

      在您的情况下,将输入字段放置在 ng-transclude 内会导致它仅编辑该范围内的值,而不是父范围内的值(这是从中提取“测试”指令的计数的地方) .

      顺便说一句,这可能是中继器 (ng-repeat) 以及其他指令的问题。最好使用诸如“Batarang”之类的工具来查找此类问题。它允许您查看每个范围内的内容并确定屏幕未显示“正确”数据的原因。希望有助于进一步解释!

      【讨论】:

      • 值不会被复制。
      • @zeroflagL 我相信您可以更新,但为什么您可以访问在父范围中设置的子范围中的 ?似乎存在一种“单向”副本,如果尚未在子项中设置这些值,则可以继承这些值。也许你可以放一个可以解释的链接!
      • 它的工作原理是原型继承。我最终决定给出自己的答案来稍微解释一下技术细节。
      【解决方案4】:

      似乎我们无法覆盖它,因为ngTransclude 将直接使用$transclude 函数。 见:https://github.com/angular/angular.js/blob/master/src/ng/directive/ngTransclude.js

      和:http://docs.angularjs.org/api/ng.$compile

      transcludeFn - 一个预先绑定到正确的嵌入范围的嵌入链接函数。范围可以被可选的第一个参数覆盖。这与指令控制器的 $transclude 参数相同。函数([范围],cloneLinkingFn)。

      【讨论】:

        【解决方案5】:

        对我来说,这似乎纯粹是一个范围问题。让我们看看两者生成的标记:

        不工作:

        <body ng-app="App" class="ng-scope">
          <h1 class="ng-binding">Outer1 <div ng-transclude="">
            <h3 class="ng-scope ng-binding">Inner 1</h3>
            <input type="text" ng-model="count" class="ng-scope ng-pristine ng-valid">
            </div>
          </h1>
        </body>
        

        工作:

        <body ng-app="App" class="ng-scope">
          <input type="text" ng-model="count" class="ng-valid ng-dirty">
          <h1 class="ng-binding">Outer <div ng-transclude="">
            <h3 class="ng-scope ng-binding">Inner </h3>
            </div>
          </h1>
        </body>
        

        ng-scope 类是 Angular 声明新范围的有用标记。

        您可以通过标记看到,在工作示例中,count 属性都包含在附加到bodyscope 中。因此,在这种情况下,directive 范围是 body 范围的子范围(因此可以访问它)。

        但是,在不起作用的示例中,Outer1 属性位于 input 所在的范围之外。

        Angular Scope documentation 很好地涵盖了这一点。作用域按层次结构排列,子作用域可以访问父作用域(但不能反过来):

        应用程序可以有多个作用域,因为有些指令 创建新的子范围(请参阅指令文档以查看哪个 指令创建新范围)。创建新范围时,它们是 添加为其父范围的子级。这将创建一个树结构 这与它们所附加的 DOM 平行

        【讨论】:

        • 介意我问 - 你用什么工具来生成“范围”图像?看起来很不错! :)
        • 这是直接指向 Angular 文档的图片链接。 :) docs.angularjs.org/img/guide/concepts-scope.png
        • 我现在完全明白了,这完全取决于您的回答,好先生。好吧,你的和那个蛋头视频:) 所以在“工作”版本中,输入正在更新父范围,所以它们都更新但在“不工作”版本中,输入正在更新子范围,但因为它添加了一个文字属性到作用域,它掩盖了父级的属性,这就是使用对象有效的原因。
        【解决方案6】:

        顺序很重要,因为在作用域上创建属性与实际使用绑定到作用域的对象之间存在差异(尤其是当 transclude 创建新的子 scopr 时)。最佳实践是在作用域上使用一个对象并将属性绑定到该对象,当作用域问题可能与指令和 transclude 一起发挥作用时。

        如果您将代码更改为此,它将按您的预期工作,并且顺序无关紧要。请注意,我正在创建一个范围对象并将计数作为属性放在该对象上。

        <test>
            <h3>Inner {{data.count}}</h3>
            <input type="text" ng-model="data.count"/>
        </test>
        

        var App = angular.module('App', []);
        
        App.directive('test', function() {
            return {
              restrict: 'E',
              replace: true,
              transclude: true,
              template: "<h1>Outer{{data.count}} <div ng-transclude></div></h1>",
              controller: function ($scope) {
                  $scope.data = {};
                  $scope.data.count = 1;
              }
            };
          });
        

        这是一个关于这个主题的很棒的教程。 EggHead 的道具。 https://egghead.io/lessons/angularjs-the-dot

        【讨论】:

        • 感谢克里斯。
        【解决方案7】:

        ng-change 添加到 input ,它应该可以工作。问题是控制器进入指令不知道count 更改。

        JS

        var App = angular.module('App', []);
        
        App.directive('test', function () {        
            return {
                restrict: 'E',
                replace: true,
                transclude: true,
                template: "<h1>Outer {{this.count}} <div ng-transclude></div></h1>",
                controller: function ($scope) {
                    $scope.count = 1;
        
                    $scope.onChange = function(count){          
                      $scope.count = count;          
                    }
                }       
            };
        });
        

        HTML

        <test>
             <h3>Inner {{count}}</h3>
            <input type="text" ng-model="count" ng-change="onChange(count)">        
        </test>
        

        演示Fiddle

        【讨论】:

        • 感谢马克西姆的回复
        猜你喜欢
        • 1970-01-01
        • 2017-03-09
        • 2013-10-02
        • 1970-01-01
        • 2018-03-30
        • 2018-05-26
        • 1970-01-01
        • 2010-11-29
        • 2016-09-27
        相关资源
        最近更新 更多