ng-if 被调用的次数多于应有的次数

Posted

技术标签:

【中文标题】ng-if 被调用的次数多于应有的次数【英文标题】:ng-if being called more times than it should 【发布时间】:2016-05-04 13:39:38 【问题描述】:

我正在尝试根据所选类别过滤一些频道。

我有一个 channel in channels 的 ng-repeat,其中有一个 ng-if="hasCategory(channel.category)

这是文件:

db.channels.find().pretty()

    "_id" : "2kj9GK8jE9yHezoWb",
    "name" : "example name",
    "logo" : "path/to/image.svg",
    "number" : 2,
    "category" : [
        "Broadcast",
        "Premium"
    ],
    "tags" : "example tag tags"

这是处理 ng-if 的函数:

$scope.hasCategory = function (categoryNameArray) 
  console.log('thisisbeingcalled');
  var e = _.indexOf(categoryNameArray, $scope.activeCategory);
  if (e === -1) 
    return false;
   else 
    return true;
  
;

这就是我设置活动类别的方式:

$scope.setCategory = function (name) 
  $scope.activeCategory = name;
;

我通过单击视图中的按钮发送:

<section layout="row" layout-align="center center" layout-wrap ng-init="activeIndex; activeCategory">
  <md-button flex="auto" flex-sm="45" flex-xs="100" ng-repeat="kit in kits | orderBy: 'order'" ng-class="active: (activeIndex.index == $index)" class="md-raised">
    <a href="" ng-click="activeIndex.index = $index; setCategory(kit.name);" class="bold">kit.name</a>
  </md-button>
</section>

这是套件文档架构:

db.kits.find().pretty()
 "_id" : "k7JFX6DHu5GYnyer3", "name" : "Broadcast", "order" : 1 
 "_id" : "KrKRm4gysw5sfQnnr", "name" : "Premium", "order" : 2 
 "_id" : "dieukQ3NRsA44CSns", "name" : "Ultimate", "order" : 3 

现在我只有一个通道来测试这个,但是每次我单击按钮更改类别时,即使在页面启动时(第一次加载),$scope.hasCategory 函数也会被多次调用

我还有一个搜索栏来搜索当前类别中的频道,我不确定这是否也会影响。

<input type="text" ng-model="queryname" ng-model-options="debounce: 500">

现在,当我有超过 100 个频道时,这变得非常滞后,ng-if 被调用超过 10k 次,这使得页面冻结了很长一段时间。

我能做些什么来解决这个问题?

编辑

忘记添加ng-if 的位置:

<md-card flex="15" flex-xs="40" class="slide-up" layout="column" ng-repeat="channel in channels | orderBy: 'number' | filter: queryname" ng-if="hasCategory(channel.category)" ng-init="channel.edit = false">
  <md-card-header ng-show="channel.edit == false">
    <img ng-src="channel.logo" >
  </md-card-header>
  <md-card-header-text ng-show="channel.edit == false">
    <span class="md-subhead dark-blue" layout="row" layout-align="center" layout-margin>channel.number</span>
  </md-card-header-text>
</md-card>

【问题讨论】:

【参考方案1】:

您的ng-if 将在每个摘要循环中被调用。问题是它包含一个涉及函数调用的表达式。 Angular 无法知道表达式的结果何时会改变,所以它每次都会调用它。

更好的选择是在每个通道中设置一个标志并使用 ng-if 来测试相关标志。然后,您只需在 $scope.activeCategory 更改时更新标志,您可以使用设置类别的代码或使用 $scope.$watch() 自动触发它。

例如

$scope.setCategory = function (name) 
  $scope.activeCategory = name;
  for (var index=0; index < $scope.channels.length; index++) 
      $scope.channels[index].hasCategory = hasCategory($scope.channels[index].category, name);
  
;

function hasCategory(categoryNameArray, name) 
  console.log('thisisbeingcalled');
  var e = _.indexOf(categoryNameArray, name);
  if (e === -1) 
    return false;
   else 
    return true;
  

然后在你的模板中:

<md-card flex="15" flex-xs="40" class="slide-up" layout="column"
    ng-repeat="channel in channels | orderBy: 'number' | filter: queryname"
    ng-if="channel.hasCategory"
    ng-init="channel.edit = false">
  <md-card-header ng-show="channel.edit == false">
    <img ng-src="channel.logo" >
  </md-card-header>
  <md-card-header-text ng-show="channel.edit == false">
    <span class="md-subhead dark-blue" layout="row" layout-align="center" layout-margin>channel.number</span>
  </md-card-header-text>
</md-card>

应该更有效率。

【讨论】:

啊,非常感谢!我对您的代码进行了一些调整,例如$scope.channels[index].hasCategory = hasCategory($scope.channels[index].category, name);,现在它可以工作了,另一个问题是,如何设置起始类别,以便用户不必选择类别即可查看频道。 在你的方法 hasCategory() 中。您应该将返回值更改为 return !(e === -1) 。它更具可读性 @Riverside,同意这将是一种改进,但是当我回答问题时,我会尽量将原始代码更改保持在最低限度,否则存在掩盖答案实际所需更改的风险。 @Duncan 你是对的,这完全有道理。就像你说的,这只是一点点改进。 是否会在每个摘要循环中调用类似ng-if = "$ctrl.categories.includes(channel)" 的内容?其中categories 是字符串列表,channel 是字符串。

以上是关于ng-if 被调用的次数多于应有的次数的主要内容,如果未能解决你的问题,请参考以下文章

剑指Offer - 判断数组中是否有一个数出现次数多于一半

Educational Codeforces Round 85 (Div. 2) 部分题解

当方法被意外调用的次数超过指定次数时,有没有办法从 rspec 获取堆栈跟踪?

构造函数被调用的次数

要使循环体至少执行一次,应使用啥循环结构

计算函数被调用的次数