【问题标题】:Angular: infinite digest loop in filterAngular:过滤器中的无限摘要循环
【发布时间】:2014-02-27 11:19:43
【问题描述】:

我正在编写一个自定义 Angular 过滤器,它随机将传递给它的输入大写。

代码如下:

angular.module('textFilters', []).filter('goBananas', function() {
  return function(input) {

    var str = input;
    var strlen = str.length;

    while(strlen--) if(Math.round(Math.random())) {
      str = str.substr(0,strlen) + str.charAt(strlen).toUpperCase() + str.substr(strlen+1);
    }

    return str;
  };
});

在我看来,我这样称呼它:

    <a class='menu_button_news menu_button' ng-href='#/news'>
        {{"News" | goBananas}}
    </a>

它有效,但在我的控制台中我看到了一个 rootScope:infdig (infinite digest) 循环。

我在理解为什么会发生这种情况以及我能做些什么来解决它时遇到了一些麻烦。如果我理解正确,这是因为该函数调用了超过 5 个摘要操作。但是输入只被过滤器调用一次,对吧?

任何帮助表示赞赏。

【问题讨论】:

  • 你看过我的回答吗?一点用都没有,想删。但是,我觉得这是非常好的信息,我们应该 memoizing 这样的过滤器来保持数据转换与修复无限摘要错误的逻辑分开。这样代码就干净多了。我非常感谢您的反馈。请注意,我的解决方案不涉及更改您的代码,效果很好。

标签: angularjs


【解决方案1】:

问题是过滤器每次调用都会产生一个新的结果,Angular 会多次调用它以确保值完成更改,但它永远不会。例如,如果您对单词'stuff' 使用uppercase 过滤器,则结果为'STUFF'。当 Angular 再次调用过滤器时,结果又是'STUFF',因此摘要循环可以结束。例如,将其与返回 Math.random() 的过滤器进行对比。

技术解决方案是在控制器中而不是在视图中应用转换。但是,我更喜欢使用过滤器转换视图中的数据,即使过滤器像您一样应用了不稳定的转换(每次返回不同)。

在大多数情况下,可以通过记忆过滤器功能来修复不稳定的过滤器。下划线和 lodash 包含一个 memoize 函数。您只需将其包裹在 filter 函数中,如下所示:

.filter('myFilter', function() {
  return _memoize(function(input) {
    // your filter logic
    return result;
  });
});

【讨论】:

  • 你能告诉我这里出了什么问题吗-stackoverflow.com/questions/30435382/…
  • @codebased my answer here 解释了您遇到的问题和最简单的解决方案。将过滤器包装在 memoize 函数中。
【解决方案2】:

由于摘要将继续运行,直到达到模型的一致状态或将运行 10 次迭代,您需要自己的算法来生成伪随机数,该算法将为相同的字符串返回相同的数字以避免无限消化循环。如果算法将使用字符值、字符位置和一些可配置的种子来生成数字,那就太好了。避免在此类算法中使用日期/时间参数。这是一种可能的解决方案:

HTML

<h1>{{ 'Hello Plunker!' | goBananas:17 }}</h1> 

JavaScript

angular.module('textFilters', []).
  filter('goBananas', function() {
    return function(input, seed) {
      seed = seed || 1;
      (input = input.split('')).forEach(function(c, i, arr) {
        arr[i] = c[(c.charCodeAt(0) + i + Math.round(seed / 3)) % 2 ? 'toUpperCase' : 'toLowerCase']();
      });
      return input.join('');
    }
  });

您可以使用seed 参数来更改算法。例如它可能是$indexngRepeat

Plunker:http://plnkr.co/edit/oBSGQjVZjhaIMWNrPXRh?p=preview

【讨论】:

  • 太好了,这很有魅力。难道不能定义 $scope.random = Math.random()*10;在控制器中并在视图中使用过滤器作为 {{'Hey there' | goBananas:random}} ?
  • @Sqrler 是的,看看这个 plunk plnkr.co/edit/ee0mRPMhfFsDZMzZv9iE?p=preview
【解决方案3】:

如果您希望行为真正随机,另一种方法是在链接期间通过创建种子只处理一次随机性,然后在实际过滤器中使用seeded random number generator

angular.module('textFilters', []).filter('goBananas', function() {
  var seed = Math.random()
  var rnd = function () {
    var x = Math.sin(seed++) * 10000;
    return x - Math.floor(x);
  }

  return function(input) {

    var str = input;
    var strlen = str.length;

    while(strlen--) if(Math.round(rnd())) {
      str = str.substr(0,strlen) + str.charAt(strlen).toUpperCase() + str.substr(strlen+1);
    }

    return str;
  };
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-07
    • 1970-01-01
    相关资源
    最近更新 更多