angularjs 如何“取消监视”表达式

noj0wjuj  于 2022-12-26  发布在  Angular
关注(0)|答案(5)|浏览(138)

假设我有一个大数组的ng-repeat。
当ng-repeat运行时,它会将数组的每个元素添加到一个独立的作用域中,同时将数组本身也包含在一个作用域中,这意味着$digest会检查整个数组的变化,并且在此基础上,它会检查数组中的 * 每个元素 * 的变化。
请将this plunker作为我所讨论的内容的示例。
在我的用例中,我从不更改数组中的任何一个元素,所以我不需要监视它们。我只会更改整个数组,在这种情况下,ng-repeat将重新呈现整个表。(如果我错了,请告诉我。)
在一个1000行的数组中,还有1000个表达式不需要计算。

如何在监视主数组的同时从监视器注销每个元素?

也许我可以更好地控制我的$digest并跳过每一行,而不是注销?
这个具体的例子实际上是一个更普遍的问题的例子,我知道$watch returns a 'deregisteration' function,但当一个指令注册手表时,这是没有帮助的,这是大多数时候。

wlsrxk51

wlsrxk511#

    • 让一个不需要监视的大型数组的中继器监视每个项目。**

您需要创建一个自定义指令,该指令将一个参数和表达式添加到数组中,然后在链接函数中,您只需查看该数组,并让链接函数以编程方式刷新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>');
              });
           });
        }
    };
});

......这看起来像是个麻烦。

    • 备用黑客方法**

你可以循环$scope.$$watchers,检查$scope.$$watchers[0].exp.exp是否匹配你想要删除的表达式,然后用一个简单的splice()调用来删除它。这里的PITA是,像Blah {{whatever}} Blah这样的标签之间的东西将是表达式,甚至包括回车符。
从好的方面来说,你可以在ng-repeat的$作用域中循环,删除所有内容,然后显式地添加你想要的手表......我不知道。
不管怎样,这看起来像是黑客。

    • 删除由$scope. $watch创建的观察程序**

可以使用$watch调用返回的函数注销$watch
例如,要使$watch仅激发一次:

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

当然,您可以随时调用unregister函数......这只是一个示例。

    • 结论:没有一种好方法可以完全满足您的要求**

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

    • 编辑2(删除坏主意)**
2wnc66cl

2wnc66cl2#

$watch返回一个函数,在调用时解除$watch的绑定。因此,这就是“watchOnce”所需的全部内容:

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

4nkexdtk3#

编辑:看我贴出的另一个答案。
我已经用一种独立的方式实现了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>
    • 注**
hjqgdpho

hjqgdpho4#

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

edit:一些警告:

如果在模板中使用{{}}插值,则需要将其替换为<span bo-text>
如果您使用的是ng-指令,则需要将其替换为正确的bo-指令。
此外,如果要将bindonceng-repeat放在同一个元素上,则应该尝试将bindonce移动到父元素(参见https://github.com/Pasvaz/bindonce/issues/25#issuecomment-25457970),或者将track by添加到ng-repeat

llycmphe

llycmphe5#

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

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

这将绑定该值,并在运行第一个摘要周期且该值第一次从undefined更改为defined时删除监视器。
这是一个非常有用的BLOG

相关问题