Angular Directive的模板绑定不会更新

时间:2014-01-03 14:05:37

标签: angularjs angularjs-directive

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

<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

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

非常感谢

7 个答案:

答案 0 :(得分:2)

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

无效:

<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属性都附加在scope附加的body中。因此,在这种情况下,directive范围是body范围的子级(因此可以访问它)。

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

Angular Scope documentation涵盖了这一点。范围以层次结构排列,子范围可以访问父范围(但不是相反):

  

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

angular scope heirarchy

答案 1 :(得分:2)

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

在您的情况下,将输入字段放在ng-transclude中会导致仅编辑该范围中的值,而不是父范围中的值(这是“test”指令的计数从中拉出的位置)

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

答案 2 :(得分:1)

这是a work around

$scope.count更改为

$scope.helper = {
    count: 1
}

并重构其余部分。

Check this video out获得解释。

答案 3 :(得分:1)

顺序很重要,因为在范围上创建属性与实际使用绑定到范围的对象之间存在差异(特别是当转换创建新的子scopr时)。最佳做法是使用范围上的对象,并在范围问题与指令和转换相关时将属性绑定到该对象。

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

<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

答案 4 :(得分:1)

ng-change添加到input,它应该有效。问题是controller into指令不知道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

答案 5 :(得分:1)

这是一个范围问题。

$scope.count = 1;将属性count添加到<test>所在的范围。让我们将其称为父范围。

ng-transclude创建一个新范围,让我们称之为子范围。在评估<h3>Inner {{count}}</h3>时,子范围没有属性count,因此它从父范围读取。

<input type="text" ng-model="count">将输入值绑定到子范围中的属性count。一旦你输入了东西,如果它还没有,那么它就会被创造出来。从<h3>Inner {{count}}</h3>开始,从子范围获取其值。

angular中的范围是简单的JavaScript对象,并通过原型连接到它们的父对象。因此,在您输入内容之前,子范围看起来像

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

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

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

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

答案 6 :(得分:0)

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

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

  

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