如何'揭开'表达

时间:2012-11-30 19:12:27

标签: angularjs

说我有一个大阵列的ng-repeat。

当ng-repeat运行时,它会将该数组的每个元素添加到隔离范围,并将数组本身放在范围内。这意味着$ digest会检查整个数组是否有变化,最重要的是,它会检查该数组中的每个元素是否有变化。

请参阅this plunker作为我所谈论的一个例子。

在我的用例中,我从不更改阵列的单个元素,因此我不需要让它们观看。我只会改变整个数组,在这种情况下,ng-repeat会重新渲染表格。 (如果我对此有误,请告诉我..)

在(比如说)1000行的数组中,还有1000多个我不需要评估的表达式。

如何在观看主阵列的同时取消观察者中的每个元素?

也许不是取消注册我可以更好地控制我的$ digest并以某种方式跳过每一行?

这个具体案例实际上是一个更普遍的问题的例子。我知道$watch returns a 'deregisteration' function,但是当指令正在注册手表时这并没有帮助,这在大多数情况下都是如此。

5 个答案:

答案 0 :(得分:89)

要有一个大型阵列的转发器,您不会观看每个项目。

你需要创建一个自定义指令,它接受一个参数,表达式到你的数组,然后在你刚刚看到那个数组的链接函数中,你就可以通过编程方式刷新HTML(而不是而不是使用ng-repeat)

类似(伪代码):

app.directive('leanRepeat', function() {
    return {
        restrict: 'E',
        scope: {
           'data' : '='
        },
        link: function(scope, elem, attr) {
           scope.$watch('data', function(value) {
              elem.empty(); //assuming jquery here.
              angular.forEach(scope.data, function(d) {
                  //write it however you're going to write it out here.
                  elem.append('<div>' + d + '</div>');
              });
           });
        }
    };
});

......这似乎是一个痛苦的屁股。

替代hackish方法

您可以循环浏览$scope.$$watchers并检查$scope.$$watchers[0].exp.exp以查看它是否与您要删除的表达式匹配,然后通过简单的splice()调用将其删除。这里的PITA是标签之间的Blah {{whatever}} Blah之类的东西将是表达式,甚至包括回车。

从好的方面来说,你可能只需循环遍历ng-repeat的$ scope并删除所有内容,然后明确添加你想要的手表......我不知道。

无论哪种方式,它似乎都是黑客。

删除$ scope制作的观察者。$ watch

您可以使用$watch调用返回的函数取消注册$watch

例如,要让$watch只触发一次:

var unregister = $scope.$watch('whatever', function(){ 
     alert('once!');
     unregister();
});

当然,您可以随时调用取消注册功能......这只是一个例子。

结论:没有一个很好的方法可以完全满足您的要求

但有一点需要考虑:它是否值得担心?此外,将数千条记录分别加载到数十个DOMElements中真的是一个好主意吗?值得深思。

我希望有所帮助。


编辑2(删除了不好的主意)

答案 1 :(得分:15)

$ watch返回一个在调用时取消绑定$ watch的函数。所以这就是“watchOnce”所需要的一切:

var unwatchValue = scope.$watch('value', function(newValue, oldValue) {
  // Do your thing
  unwatchValue();
});

答案 2 :(得分:8)

编辑:看到我发布的其他答案。

我已经离开并以可分离的方式实施了blesh的想法。我的ngOnce指令只会破坏ngRepeat在每个项目上创建的子范围。这意味着范围无法从其父母scope.$digest到达,并且观察者永远不会被执行。

Source and example on JSFiddle

指令本身:

angular.module('transclude', [])
 .directive('ngOnce', ['$timeout', function($timeout){
    return {
      restrict: 'EA',
      priority: 500,
      transclude: true,
      template: '<div ng-transclude></div>',
        compile: function (tElement, tAttrs, transclude) {
            return function postLink(scope, iElement, iAttrs, controller) {
                $timeout(scope.$destroy.bind(scope), 0);
            }
        }
    };
}]);

使用它:

      <li ng-repeat="item in contents" ng-once>
          {{item.title}}: {{item.text}}
      </li>

注意 ng-once没有创建自己的范围,这意味着它可以影响兄弟元素。这些都做同样的事情:

  <li ng-repeat="item in contents" ng-once>
      {{item.title}}: {{item.text}}
  </li>
  <li ng-repeat="item in contents">
      <ng-once>
          {{item.title}}: {{item.text}}
      </ng-once>
  </li>
  <li ng-repeat="item in contents">
      {{item.title}}: {{item.text}} <ng-once></ng-once>
  </li>

注意 this may be a bad idea

答案 3 :(得分:3)

您可以将bindonce指令添加到ng-repeat。您需要从https://github.com/pasvaz/bindonce下载。

编辑:一些警告:

如果您在模板中使用{{}}插值,则需要将其替换为<span bo-text>

如果您使用ng-指令,则需要使用正确的bo-指令替换它们。

此外,如果您将bindonceng-repeat放在同一元素上,则应尝试将bindonce移至父元素(请参阅https://github.com/Pasvaz/bindonce/issues/25#issuecomment-25457970)或将track by添加到您的ng-repeat

答案 4 :(得分:2)

如果您使用的是angularjs 1.3或更高版本,则可以使用单一绑定语法

<li ng-repeat="item in ::contents">{{item}}</li>

这将绑定值,并在第一次摘要周期运行后删除观察者,并且值首次从undefined更改为defined

对此非常有用BLOG