Директива angularjs: $rootScope: ошибка infdig
Я пытаюсь создать директиву разбиения на страницы с помощью angularjs 1.2.15:
Это мое мнение:
<input type="text" ng-model="filter.user">
<input type="number" ng-model="filter.limit" ng-init="filter.limit=5">
<ul>
<li ng-repeat="user in (filteredUsers = (users | orderBy:order:reverse | filter:filter.user ) | limitTo: filter.limit)" ng-click="showDetails(user)">
{{user.id}} / {{user.firstname}} {{user.lastname}}
</li>
</ul>
<div pagination data='filteredUsers' limit='filter.limit'></div>
и вот моя директива разбиения на страницы:
app.directive('pagination', function(){
return {
restrict: 'A',
templateUrl: 'partials/pagination.html',
scope: {
data: '=data',
limit: '=limit'
}
}
})
Все работает отлично, без директивы разбиения на страницы. Однако с моей новой директивой, как только я загружаю страницу, я получаю ошибку $rootScope:infdig
, которую я не понимаю, поскольку директива не делает ничего, чтобы манипулировать данными, которые могут закончиться бесконечным циклом.
В чем проблема и как я могу ее решить? Спасибо!
Update:
Вот контроллер и ресурс.
Контроллер:
usersModule.controller('usersController',
function ($scope, Users) {
function init(){
$scope.users = Users.get();
}
init();
})
Ресурс (получает пользователей как массив из REST API):
app.factory('Users', function($resource) {
return $resource('http://myrestapi.tld/users', null,{
'get': { method:'GET', isArray: true, cache: true }
});
});
Обновление 2
Вот демо: http://plnkr.co/edit/9GCE3Kzf21a7l10GFPmy?p=preview
Просто введите букву (например, "f" ) в левый вход.
Ответы
Ответ 1
Проблема не в директиве, она находится в $watch, который создает директива.
Когда вы отправляете filterUsers в директиву, директива создает следующую строку:
$scope.$watch("filteredUsers", function() {
// Directive Code...
});
Обратите внимание в следующем примере, как мы воспроизводим его без директивы:
http://plnkr.co/edit/uRj19PyXkvnLNyh5iY0j
Причина, по которой это происходит, заключается в том, что вы меняете фильтрованные пользователи каждый раз, когда выполняется дайджест (поскольку вы помещаете назначение в инструкцию ng-repeat).
Чтобы исправить это, вы можете рассмотреть возможность просмотра и фильтрации массива в контроллере с дополнительным параметром "true" для $watch:
$scope.$watch("users | orderBy:order:reverse | filter:filter.user", function(newVal) {
$scope.filteredUsers = newVal;
}, true);
Вы можете проверить решение здесь:
http://plnkr.co/edit/stxqBtzLsGEXmsrv3Gp6
$watch без дополнительного параметра (true) сделает простое сравнение с объектом, и поскольку вы создаете новый массив в каждом цикле дайджеста, объект всегда будет отличаться.
Когда вы передаете истинный параметр функции $watch, это означает, что он действительно сделает глубокое сравнение с объектом, который возвращается перед запуском $watch снова, поэтому, даже если у вас есть разные экземпляры массивов, которые имеют одинаковые данные, он будет считайте их равными.
Ответ 2
Быстрое исправление заключается в том, чтобы добавить директиву $watchCollection
в директиве вместо двухсторонней привязки.
app.directive('pagination', function($parse){
return {
restrict: 'A',
template: '',
scope: {
limit: '=limit'
},
link: function(scope, elem, attrs) {
var dataExpr = $parse(attrs.data);
var deregister = scope.$parent.$watchCollection(dataExpr, function(val) {
scope.data = val;
});
scope.$on('$destroy', deregister);
}
}
})
$watchCollection
контролирует содержимое массива, а не ссылку на него.
Посмотрите, как работает здесь.
Вообще говоря, мне не нравятся такие выражения:
filteredUsers = (users | orderBy:order:reverse | filter:filter.user )
внутри просмотров. Представления должны отображать только свойства $scope
, а не создавать новые.
Ответ 3
Эта ошибка может быть удалена, чтобы очистить историю браузера от установки. Я получил одну и ту же проблему и применил много решений для решения этой проблемы, но не могу решить.
Но когда я удаляю историю браузера и кеш, эта проблема решена. Пусть это поможет вам.