2013-03-22 21 views
30

Ho problemi seri a volte con AngularJS. Così ho un array di base nel mio controller comeAngularJS non aggiorna ngRepeat durante l'aggiornamento dell'array

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

Sono ngRepeating nel mio modello sugli elementi dell'array ng-repeat = "articolo disponibile articoli". Super straightfoward finora. Dopo un paio di azioni UX, voglio spingere alcune cose nuove al mio array.

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

Quindi, il 50% delle volte, il nuovo elemento viene aggiunto alla vista. Ma l'altro 50%, non succede nulla. Ed è super frustrante; bc se lo inserisco in $ scope. $ apply(), ho ricevuto un errore "$ digest in corso". Non è d'aiuto nemmeno avvolgerlo in $ timeout.

E quando controllo l'ambito del mio elemento utilizzando l'estensione di Chrome; Posso vedere i nuovi dati sono lì e il valore $ scope.items è corretto. Ma la visione non si preoccupa di aggiungerla al DOM.

Grazie!

+0

puoi creare una demo su http://jsfiddle.net/ –

+2

La sua sicuramente correlata al ciclo di digestione. Abbiamo bisogno che tu inserisca il codice che usi per aggiornare la matrice. –

+2

Anche io sto avendo questo problema, mi piacerebbe una risposta! – Samuel

risposta

21

Si sta modificando l'oscilloscopio al di fuori del ciclo $digest di angular il 50% delle volte.

Se è presente una richiamata che non proviene da angularjs; (jambery posibbly). È necessario chiamare $apply per forzare un ciclo $digest. Ma non è possibile chiamare $apply mentre si è nel ciclo $digest poiché ogni modifica apportata verrà automaticamente riflessa.

È necessario sapere quando la richiamata non è da angolare e deve chiamare solo $apply solo allora.

Se non si conosce e non in grado di imparare, qui è un trucco:

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

Credo che dovresti evitare di controllare '$ scope. $$ phase'. È una variabile interna e, se possibile, non dovresti fare affidamento sul tuo codice. La soluzione migliore sarebbe utilizzare '$ apply', dove si è assolutamente certi che non ci saranno' $ apply 'già in corso. Ed è per questo che sto chiedendo i codici che potrebbero aggiornare l'ambito. –

+0

Non è un buon modo per codificare, sono d'accordo. Tuttavia, funziona e '$$ fase' è molto coerente e affidabile; almeno non romperà il tuo codice in modo imprevisto, tranne il fatto che può essere cambiato tra le versioni. –

+0

Lo è. L'ho già usato davvero. La domanda è: non sappiamo se questo si romperà nella prossima versione secondaria, per il suo controller di stato interno. Sto solo riflettendo sul fatto che probabilmente c'è un posto migliore per mettere "$ apply" in modo sicuro. –

0

Ho avuto lo stesso problema, e la mia correzione è stata a guardare che i controllori sono chiamati nelle direttive nidificate.

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

Il bug qui è la direttiva annidata crea un secondo controllore in catena di prototipi (un duplicato di storeController), che il modello di genitore non ha accesso a. Per risolvere scrivere il controller nidificato in questo modo:

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

Ci sono modi migliori per creare la catena di ereditarietà, ma questo risolverà il problema per molte persone AngularJS apprendimento.

4

Come sottolineato da Umur Kontacı, a volte si stanno apportando modifiche al modello al di fuori del ciclo di digestione. Tuttavia, invece di aggirare questo problema e cercare di rilevare se ci si trova in un contesto apply/digest o no, ti suggerisco di assicurarti che ciò non accada mai.

La causa principale di questo problema è che la funzione viene chiamata come reazione a un evento DOM. Per esempio.

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

Qui è dove $ apply() deve andare, non all'interno della funzione. Altrimenti, il tuo intero codice sarà disseminato di tali distinzioni prima o poi.Presumere che vi sia un ambito nel contesto di questo gestore di eventi, è possibile scrivere:

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

Tuttavia, v'è un avvertimento che devi essere a conoscenza: quando si attiva un evento click dal codice, verrà eseguito nel problema che è già in corso un ciclo di digestione. Questo è dove hai bisogno del timeout $. Le risposte a this question riguardano molto bene questo problema.

Problemi correlati