AngularJS not refreshing ngRepeat when updating array

风流意气都作罢 提交于 2019-12-02 20:05:23

You are modifying the scope outside of angular's $digest cycle 50% of the time.

If there is a callback which is not from angularjs; (posibbly jquery). You need to call $apply to force a $digest cycle. But you cannot call $apply while in $digest cycle because every change you made will be reflected automatically already.

You need to know when the callback is not from angular and should call $apply only then.

If you don't know and not able to learn, here is a neat trick:

var applyFn = function () {
    $scope.someProp = "123";
};
if ($scope.$$phase) { // most of the time it is "$digest"
    applyFn();
} else {
    $scope.$apply(applyFn);
}
lex82

As pointed out by Umur Kontacı, you are making model changes outside of the digest cycle sometimes. However, instead of working around this problem and trying to detect whether you are in an apply/digest context or not, I suggest you make sure this never happens.

The main cause for this problem is that your function is called as a reaction to a DOM event. E.g.

jQuery('.some-element').click(function() { seeminglyProblematicCode() })

This is where your $apply() needs to go, not within the function. Otherwise your whole code will be littered with such distinctions sooner or later. Assuming there is a scope in the context of this event handler, you can write:

jQuery('.some-element').click(function() { 
    $scope.$apply(function() { seeminglyProblematicCode() })
})

However, there is one caveat you have to be aware of: When you trigger a click event from your code, you will run into the problem that a digest cycle is already in progress. This is where you need the $timeout. The answers to this question cover this problem very well.

I had the same issue, and my fix was to watch which controllers are being called in nested directives.

# 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>

The bug here is the nested directive creates a second controller in prototype chain (a duplicate of storeController), which the parent template doesn't have access to. To resolve write the nested controller like so:

# Nested Directive
app.directive 'storeFront', ($scope, config) ->
  restrict: 'E'
  templatUrl: 'store-front.html'

There are better ways to create the inheritance chain, but this will resolve the issue for many people learning AngularJS.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!