ng-if вызывается больше раз, чем нужно

Я пытаюсь отфильтровать некоторые каналы в зависимости от выбранной категории.

У меня есть ng-repeat channel in channels, у которого есть 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 вызывается более 10 тысяч раз, что приводит к зависанию страницы на некоторое время.

Что я могу сделать, чтобы решить эту проблему?

ИЗМЕНИТЬ

Забыл добавить, где был 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}}" alt="">
  </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>

person Johhan Santana    schedule 27.01.2016    source источник


Ответы (1)


Ваш ng-if будет вызываться в каждом цикле дайджеста. Проблема в том, что он содержит выражение, включающее вызов функции. У Angular нет способа узнать, когда результат выражения может измениться, поэтому он вызывает его каждый раз.

Лучшим вариантом было бы установить флаг в каждом из каналов и использовать ng-if просто для проверки соответствующего флага. Затем вам просто нужно обновлять флаги всякий раз, когда изменяется $scope.activeCategory, что вы можете сделать либо с кодом, в котором вы устанавливаете категорию, либо с помощью $scope.$watch() для автоматического запуска.

e.g.

$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}}" alt="">
  </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>

должно быть более эффективным.

person Duncan    schedule 27.01.2016
comment
ааа, спасибо большое! Я сделал некоторые изменения в вашем коде, например $scope.channels[index].hasCategory = hasCategory($scope.channels[index].category, name);, и теперь он работает, другой вопрос заключается в том, как установить начальную категорию, чтобы пользователю не нужно было выбирать категорию для просмотра каналов. - person Johhan Santana; 27.01.2016
comment
в вашем методе hasCategory(). Вы должны изменить возвращаемое значение на return !(e === -1) . Это более читабельно - person Riverside; 18.08.2016
comment
@Riverside, согласен, это было бы улучшением, но когда я отвечаю на вопросы, я стараюсь свести изменения кода по сравнению с оригиналом к ​​минимуму, иначе есть риск скрыть изменения, фактически необходимые для ответа. - person Duncan; 06.09.2016
comment
@Duncan Ты прав, и в этом есть смысл. Это всего лишь небольшое улучшение, как вы сказали. - person Riverside; 06.09.2016
comment
Будет ли что-то вроде ng-if = "$ctrl.categories.includes(channel)" также вызываться в каждом цикле дайджеста? Где categories — список строк, а channel — строка. - person Mel; 21.09.2017