Как "вывести" выражение

Скажем, у меня есть ng-repeat с большим массивом.

Когда ng-repeat запускается, он добавляет каждый элемент этого массива в изолированную область, а также имеет сам массив в области. Это означает, что $digest проверяет весь массив на изменения, и, кроме того, он проверяет каждый отдельный элемент в этом массиве для изменений.

Смотрите этот плункер в качестве примера того, о чем я говорю.

В моем случае использования я никогда не меняю ни одного элемента моего массива, поэтому мне не нужно их просматривать. Я только когда-либо изменю весь массив, и в этом случае ng-repeat будет повторно отображать таблицу целиком. (Если я ошибаюсь, сообщите мне.)

В массиве (скажем) 1000 строк, это еще 1000 выражений, которые мне не нужны.

Как я могу отменить регистрацию каждого элемента из наблюдателя при просмотре основного массива?

Возможно, вместо того, чтобы отменять регистрацию, я мог бы больше контролировать мой $digest и каким-то образом пропускать каждую отдельную строку?

Этот конкретный случай на самом деле является примером более общей проблемы. Я знаю, что $watch возвращает функцию "deregisteration" , но это не помогает, когда директива регистрирует часы, что в большинстве случаев.

Ответ 1

Чтобы иметь ретранслятор с большим массивом, который вы не смотрите на каждый элемент.

Вам нужно создать настраиваемую директиву, которая принимает один аргумент, и выражение для вашего массива, а затем в функции привязки, которую вы просто наблюдаете за этим массивом, и у вас будет функция связывания, программно обновляющая HTML (скорее чем использование ng-repeat)

что-то вроде (psuedo-code):

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 между тегами, будут выражением и даже будут включать возврат каретки.

В верхней части вы можете просто пропустить пробел $n вашего ng-repeat и просто удалить все, а затем явно добавить часы, которые вы хотите... Я не знаю.

В любом случае это кажется взломом.

Чтобы удалить наблюдателя из $scope. $watch

Вы можете отменить регистрацию $watch с помощью функции, возвращаемой вызовом $watch:

Например, для того, чтобы один раз срабатывал $watch только огонь:

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

Вы можете, конечно же, вызвать функцию незарегистрирования в любое время... это был всего лишь пример.

Заключение: на самом деле нет отличного способа сделать то, что вы просите

Но одно можно подумать: стоит ли даже беспокоиться? Кроме того, действительно ли это хорошая идея, чтобы тысячи записей загружались в десятки DOMElements каждый? Пища для размышлений.

Я надеюсь, что это поможет.


EDIT 2 (удалена плохая идея)

Ответ 2

$watch возвращает функцию, которая отбрасывает $watch при вызове. Так что это все, что вам нужно для "watchOnce":

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

Ответ 3

Изменить: см. другой ответ, который я разместил.

Я пошел и реализовал идею Блеста в отличном ключе. Моя директива ngOnce просто уничтожает область дочернего объекта, которая ngRepeat создает для каждого элемента. Это означает, что область охвата не достигается от родителей scope.$digest, и наблюдатели никогда не выполняются.

Источник и пример 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>

Примечание это может быть плохая идея

Ответ 4

Вы можете добавить директиву bindonce в свой ng-repeat. Вам нужно загрузить его из https://github.com/pasvaz/bindonce.

изменить: несколько оговорок:

Если вы используете интерполяцию {{}} в своем шаблоне, вам нужно заменить ее на <span bo-text>.

Если вы используете директивы ng-, вам необходимо заменить их на правильные директивы bo-.

Кроме того, если вы помещаете bindonce и ng-repeat в один и тот же элемент, попробуйте либо переместить bindonce в родительский элемент (см. https://github.com/Pasvaz/bindonce/issues/25#issuecomment-25457970) или добавьте track by к ng-repeat.

Ответ 5

Если вы используете angularjs 1.3 или выше, вы можете использовать синтаксис одиночного связывания как

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

Это приведет к привязке значения и удалит наблюдателей после запуска первого цикла дайджест, и значение изменится с undefined на defined в первый раз.

Очень полезный БЛОГ на этом.