Как "вывести" выражение
Скажем, у меня есть 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
в первый раз.
Очень полезный БЛОГ на этом.