Angular:过滤器中的无限摘要循环

时间:2014-02-27 11:19:44

标签: angularjs

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

以下是代码:

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个摘要动作。但输入只被过滤器调用一次,对吗?

任何帮助表示感谢。

3 个答案:

答案 0 :(得分:6)

问题是过滤器每次调用时都会产生一个新的结果,而Angular会多次调用它来确保值的变化是永远不变的。例如,如果您对单词uppercase使用'stuff'过滤器,则结果为'STUFF'。当Angular再次调用过滤器时,结果再次为'STUFF',因此摘要周期可以结束。例如,使用返回Math.random()的过滤器进行对比。

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

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

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

答案 1 :(得分:4)

由于摘要将继续运行,直到达到模型的一致状态或将运行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参数来更改算法。例如,它可能是$index

ngRepeat

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

答案 2 :(得分:0)

另一种方法是,如果您希望行为真正随机,则在链接期间仅通过创建种子处理随机性一次,然后在实际过滤器中使用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;
  };
});