it-swarm.com.de

AngularJS aktualisiert ngRepeat nicht, wenn das Array aktualisiert wird

Ich habe manchmal ernsthafte Probleme mit AngularJS. Also ich habe ein einfaches Array in meinem Controller

$scope.items = ["a","b","c"]

Ich bin ngRepeating in meiner Vorlage über das Artikelarray ng-repeat = "item in items". Super geradlinig bis jetzt. Nach ein paar UX-Aktionen möchte ich ein paar neue Sachen in mein Array schieben.

 $scope.items.Push("something");

In 50% der Fälle wird das neue Element der Ansicht hinzugefügt. Aber die anderen 50% passiert nichts. Und es ist wie super frustrierend; bc wenn ich das innerhalb von $ scope einwickle. $ apply (), ich habe die Fehlermeldung "$ digest already in progress" erhalten. Das in $ Timeout zu packen hilft auch nicht.

Und wenn ich meinen Elementbereich mit der Chrome-Erweiterung überprüfe; Ich kann sehen, dass die neuen Daten vorhanden sind und der $ scope.items-Wert korrekt ist. Aber die Ansicht kümmert sich einfach nicht darum, das DOM hinzuzufügen.

Vielen Dank!

31
spacenick

Sie ändern den Gültigkeitsbereich außerhalb des $digest-Zyklus von angle in 50% der Zeit.

Wenn es einen Rückruf gibt, der nicht von anglejs stammt; (wahrscheinlich jquery). Sie müssen $apply aufrufen, um einen $digest-Zyklus zu erzwingen . Sie können $apply jedoch nicht im $digest-Zyklus aufrufen, da jede von Ihnen vorgenommene Änderung bereits automatisch übernommen wird.

Sie müssen wissen, wann der Rückruf nicht von angle ist und $apply nur dann aufrufen sollte.

Wenn Sie es nicht wissen und nicht lernen können, haben Sie hier einen Trick:

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

Wie von Umur Kontacı ausgeführt, führen Sie manchmal Modelländerungen außerhalb des Digest-Zyklus durch. Anstatt dieses Problem zu umgehen und herauszufinden, ob Sie sich in einem Apply/Digest-Kontext befinden oder nicht, schlage ich vor, sicherzustellen, dass dies niemals der Fall ist. 

Die Hauptursache für dieses Problem ist, dass Ihre Funktion als Reaktion auf ein DOM-Ereignis aufgerufen wird. Z.B.

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

Hier muss Ihr $ apply () gehen, nicht innerhalb der Funktion. Sonst wird Ihr gesamter Code früher oder später mit solchen Unterscheidungen übersät sein. Angenommen, es gibt einen Gültigkeitsbereich im Kontext dieses Ereignishandlers, können Sie schreiben:

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

Es gibt jedoch eine Einschränkung, die Sie beachten sollten: Wenn Sie ein Klickereignis aus Ihrem Code auslösen, besteht das Problem, dass bereits ein Digest-Zyklus ausgeführt wird. Hier brauchen Sie das $ Timeout. Die Antworten auf diese Frage decken dieses Problem sehr gut ab.

4
lex82

Ich hatte das gleiche Problem, und meine Lösung bestand darin, zu beobachten, welche Controller in verschachtelten Direktiven aufgerufen werden.

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

Der Fehler hier ist, dass die verschachtelte Direktive einen zweiten Controller in der Prototypkette (ein Duplikat von storeController) erstellt, auf den die übergeordnete Vorlage keinen Zugriff hat. Um dies zu beheben, schreiben Sie den verschachtelten Controller wie folgt:

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

Es gibt bessere Möglichkeiten, eine Vererbungskette zu erstellen. Dies löst jedoch das Problem für viele Menschen, die AngularJS lernen.

0
brettu