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


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

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

посмотреть этот plunker как пример, о чем я говорю.

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

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

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

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

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

5 68

5 ответов:

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

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

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

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() звонок. Пита здесь, это такие вещи, как Blah {{whatever}} Blah между тегами будет выражение, и даже будет включать в себя возврат каретки.

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

в любом случае, это похоже на халтуру.

чтобы удалить наблюдатель, сделанный $scope.$смотреть

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

например, иметь $watch только огонь один раз:

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

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

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

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

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


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

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

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

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

Я пошел и реализовал идею блеша отдельным способом. Мой 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>

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

вы можете добавить

если вы используете angularjs 1.3 или выше, вы можете использовать синтаксис single bind как

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

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

очень полезным блог об этом.