AngularJS не обновляется ngRepeat при обновлении массива
У меня возникают серьезные проблемы с пониманием AngularJS. Итак, у меня есть базовый массив в моем контроллере, например
$scope.items = ["a","b","c"]
Я ngRepeating в моем шаблоне над массивом элементов ng-repeat = "item in items". Супер прямолинейный до сих пор. После нескольких действий UX, я хочу подтолкнуть некоторые новые вещи к моему массиву.
$scope.items.push("something");
Итак, 50% времени, новый элемент добавляется в представление. Но остальные 50% ничего не происходит. И это как супер разочарование; bc, если я обернул это значение в $scope. $apply(), я получил ошибку "$ digest is in progress". Обертка в $timeout также не помогает.
И когда я проверяю область своего элемента с помощью расширения Chrome; Я вижу, что новые данные есть, и значение $scope.items верное. Но мнение просто не заботится о добавлении этого в DOM.
Спасибо!
Ответы
Ответ 1
Вы изменяете область вне цикла angular $digest
в 50% случаев.
Если есть обратный вызов, который не является угловым; (posicbly jquery). Вы должны вызвать $apply
, чтобы принудительно выполнить цикл $digest
.
Но вы не можете вызвать $apply
в цикле $digest
, потому что каждое сделанное вами изменение будет автоматически отражено.
Вам нужно знать, когда обратный вызов не от angular и должен вызывать $apply
только тогда.
Если вы не знаете и не можете учиться, вот аккуратный трюк:
var applyFn = function () {
$scope.someProp = "123";
};
if ($scope.$$phase) { // most of the time it is "$digest"
applyFn();
} else {
$scope.$apply(applyFn);
}
Ответ 2
Как указано Umur Kontacı, вы иногда делаете изменения модели вне цикла дайджеста. Однако вместо того, чтобы обойти эту проблему и пытаться определить, находитесь ли вы в контексте приложения/дайджест или нет, я предлагаю вам убедиться, что этого никогда не произойдет.
Основной причиной этой проблемы является то, что ваша функция называется реакцией на событие DOM. Например.
jQuery('.some-element').click(function() { seeminglyProblematicCode() })
Здесь вам нужно использовать $apply(), а не внутри функции. В противном случае весь ваш код будет усеян такими различиями рано или поздно. Предполагая, что в контексте этого обработчика существует область видимости, вы можете написать:
jQuery('.some-element').click(function() {
$scope.$apply(function() { seeminglyProblematicCode() })
})
Однако есть одна оговорка, о которой вы должны знать: когда вы вызываете событие click из вашего кода, вы столкнетесь с проблемой, что цикл дайджеста уже выполняется. Здесь вам понадобится $timeout. Ответ на этот вопрос очень хорошо освещает эту проблему.
Ответ 3
У меня была такая же проблема, и я решил проверить, какие контроллеры вызываются во вложенных директивах.
# Parent Controller
app.controller 'storeController', ($scope, products) ->
$scope.cart = ["chicken", "pizza"]
$scope.addToCart = (item) ->
$scope.cart.push item
# from service
products.get().then (items) ->
$scope.products = items
# Parent Directives
app.directive 'storeContainer', ($scope, config) ->
restrict: 'E'
templatUrl: 'store-container.html'
controller: 'storeController'
# Nested Directive
app.directive 'storeFront', ($scope, config) ->
restrict: 'E'
templatUrl: 'store-front.html'
controller: 'storeController'
# Parent Template templates/directives/store-container.html
<div ng-repeat="item in cart">{{ item }}</div>
<strore-front></store-front>
# Nested Template templates/directives/store-front.html
<ul ng-repeat="item in products">
<li ng-click"addToCart(item)">{{ item }}</li>
</ul>
Ошибка здесь вложенная директива создает второй контроллер в цепочке прототипов (дубликат storeController), к которому у родительского шаблона нет доступа. Чтобы разрешить запись вложенного контроллера следующим образом:
# Nested Directive
app.directive 'storeFront', ($scope, config) ->
restrict: 'E'
templatUrl: 'store-front.html'
Есть более эффективные способы создания цепочки наследования, но это решит проблему для многих людей, изучающих AngularJS.