说我有一个大阵列的ng-repeat。
当ng-repeat运行时,它会将该数组的每个元素添加到隔离范围,并将数组本身放在范围内。这意味着$ digest会检查整个数组是否有变化,最重要的是,它会检查该数组中的每个元素是否有变化。
请参阅this plunker作为我所谈论的一个例子。
在我的用例中,我从不更改阵列的单个元素,因此我不需要让它们观看。我只会改变整个数组,在这种情况下,ng-repeat会重新渲染表格。 (如果我对此有误,请告诉我..)
在(比如说)1000行的数组中,还有1000多个我不需要评估的表达式。
如何在观看主阵列的同时取消观察者中的每个元素?
也许不是取消注册我可以更好地控制我的$ digest并以某种方式跳过每一行?
这个具体案例实际上是一个更普遍的问题的例子。我知道$watch returns a 'deregisteration' function,但是当指令正在注册手表时这并没有帮助,这在大多数情况下都是如此。
答案 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>
答案 3 :(得分:3)
您可以将bindonce
指令添加到ng-repeat
。您需要从https://github.com/pasvaz/bindonce下载。
编辑:一些警告:
如果您在模板中使用{{}}
插值,则需要将其替换为<span bo-text>
。
如果您使用ng-
指令,则需要使用正确的bo-
指令替换它们。
此外,如果您将bindonce
和ng-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。