2016-01-20 13 views
13

Ho problemi ad aggiornare il mio ambito sul front-end mentre faccio una richiesta ad un'API. Sul backend posso vedere che il valore della mia variabile $ scope sta cambiando, ma questo non si riflette nelle viste.AngularJS Scope non si sta aggiornando in vista dopo chiamata asincrona

Ecco il mio controller.

Controllers.controller('searchCtrl', 
function($scope, $http, $timeout) { 
    $scope.$watch('search', function() { 
     fetch(); 
    }); 

$scope.search = "Sherlock Holmes"; 

function fetch(){ 
    var query = "http://api.com/v2/search?q=" + $scope.search + "&key=[API KEY]&format=json"; 
    $timeout(function(){ 
     $http.get(query) 
     .then(function(response){ 
     $scope.beers = response.data; 
     console.log($scope.beers); 
     }); 
    }); 
} 
}); 

Ecco un frammento del mio html

<div ng-if="!beers"> 
    Loading results... 
</div> 
<p>Beers: {{beers}}</p> 
<div ng-if="beers.status==='success'"> 

    <div class='row'> 
    <div class='col-xs-8 .col-lg-8' ng-repeat="beer in beers.data track by $index" ng-if="beer.style"> 
    <h2>{{beer.name}}</h2>   
    <p>{{beer.style.description}}</p> 
    <hr> 
    </div> 
    </div> 
</div> 

<div ng-if="beers.status==='failure'"> 
    <p>No results found.</p> 
</div> 

Ho provato diverse soluzioni, tra cui utilizzando $ portata applicano $().; ma questo solo crea l'errore comune

Error: $digest already in progress

Il seguente post suggerisce di utilizzare $ timeout o $ asyncDefault AngularJS : Prevent error $digest already in progress when calling $scope.$apply()

Il codice che ho sopra usa $ timeout e non ho nessun errore, ma ancora il la vista non si sta aggiornando.

aiuto apprezzato

+2

Non dovresti aver bisogno del 'timeout' -' $ http' attiverà un ciclo '$ digest' - sei sicuro che i dati tornino? – tymeJV

+0

aggiungere un gestore errori alla richiesta. Controlla anche la rete degli strumenti di sviluppo per assicurarti che la richiesta abbia esito positivo e restituisca quanto previsto. Sembra un problema ajax – charlietfl

+0

Concordato che non è necessario un timeout per questo codice e che $ http dovrebbe innescare un ciclo di digest. Il tuo console.log all'interno della risposta $ http mostra $ scope.beers aggiornato? –

risposta

0

Come suggeriscono i commenti, non dovrebbe essere necessario utilizzare $timeout per innescare un ciclo digest. Finché l'UX che provoca la modifica è all'interno dei limiti di un costrutto angolare (ad esempio funzione controller, servizio, ecc.), Deve manifestarsi all'interno del ciclo di digest.

Sulla base di ciò che posso dedurre dal tuo post, stai probabilmente utilizzando un input di ricerca per colpire un'API con risultati. Ti consigliamo di modificare la logica in modo da attivare la ricerca su un evento esplicito anziché su $watch.

<input ng-model="search" ng-change="fetch()"> 

Rimuovere il $watch logica e la $timeout involucro.

function fetch(){ 
    var query = "http://api.com/v2/search?q=" + $scope.search + "&key=[API KEY]&format=json"; 
$http.get(query) 
.then(function(response){ 
    $scope.beers = response.data; 
    console.log($scope.beers); 

    //it's a good habit to return your data in the promise APIs 
    return $scope.beers; 
}); 
} 

Le ragioni che fare questa raccomandazione è:

  • Hai il controllo più fine di come il ng-change richiamata viene attivato utilizzando ng-model-options. Ciò significa che è possibile ritardare, attivare trigger per vari eventi UX, ecc.
  • Hai mantenuto una sequenza più chiara di come viene chiamato fetch.
  • Probabilmente avete evitato problemi di prestazioni e $ digest.
+0

Ehi. Mi permetta di provarlo! Forse sono troppo stravagante con l'osservatore – peterpod

+0

Spero che funzioni. Generalmente uso '$ watch' per cose che non sono attivate direttamente da qualche UX (in altre parole, non attivate da un direttiva esistente o altro costrutto ng) e nelle direttive in cui è necessario attingere agli eventi DOM in cui non desidero cablare un ambito isolato, ecc. – jusopi

+0

Ho provato il tuo suggerimento con ng-change ma ho deciso che sarò semplice qui e fare ng-submit. Indipendentemente da questo metodo, la vista non viene aggiornata. Il mio consolelog stampa una nuova risposta JSON su ogni invio di modulo ma la variabile scope non si aggiorna. Ho aggiornato il mio post sopra per mostrare il mio codice HTML – peterpod

11

Se si utilizza AngularJS 1.3+, è possibile provare $scope.$applyAsync() subito dopo la dichiarazione $scope.beers = response.data;.

Questo è ciò che la documentazione angolare dice di $applyAsync()

Schedule the invocation of $apply to occur at a later time. The actual time difference varies across browsers, but is typically around ~10 milliseconds. Source

Aggiornamento

Come altri hanno fatto notare, non si dovrebbe (in genere) hanno bisogno per innescare il ciclo digest manualmente. La maggior parte delle volte punta solo a un design sbagliato (o almeno non ad un design di AngularJS-friendly) della tua applicazione.

Attualmente nell'OP il metodo fetch viene attivato su $ watch. Se invece quel metodo dovesse essere attivato da ngChange, il ciclo di digest dovrebbe essere attivato automaticamente.

Ecco un esempio ciò che un tale codice potrebbe essere simile:

HTML

// please note the "controller as" syntax would be preferred, but that is out of the scope of this question/answer 
<input ng-model="search" ng-change="fetchBeers()"> 

JavaScript

function SearchController($scope, $http) { 

    $scope.search = "Sherlock Holmes"; 

    $scope.fetchBeers = function() { 
     const query = `http://api.com/v2/search?q=${$scope.search}&key=[API KEY]&format=json`; 
     $http.get(query).then(response => $scope.beers = response.data); 
    }; 

} 
0

Ehi ragazzi ho risolto il problema, ma io sono Non sono sicuro esattamente perché questo ha cambiato qualcosa. Riorganizzando il mio codice su JS Fiddle ho appena messo tutti i miei partial nel file index.html in questo modo e le variabili request e scope sono state aggiornate senza problemi. C'è stato forse un conflitto di controller con il mio html sopra?

<body ng-app="beerify" ng-controller='searchCtrl'> 

<nav class="navbar navbar-inverse navbar-fixed-top"> 
    <div class="container"><!-- nav bar code --> 
    </div> 
</nav> 

<!-- Main jumbotron for a primary marketing message or call to action --> 
<div class="jumbotron"> 
    <div class="container"> 
    <h1>Title</h1> 

    <form ng-submit="fetch()"> 
     <div class="input-group"> 
      <input type="text" ng-model="search" 
       class="form-control" placeholder="Search the name of a beer" name="srch-term" id="srch-term"> 
      <div class="input-group-btn"> 
       <button class="btn btn-default" type="submit"><i class="glyphicon glyphicon-search"></i></button> 
      </div> 
     </div> 
    </form> 

    </div> 
</div> 

<div class="container"> 

    <div ng-if="!beers"> 
    Loading results... 
    </div> 
    <div ng-if="beers.status==='success'"> 
    <div class='row'> 
     <div class='col-xs-8 .col-lg-8' ng-repeat="beer in beers.data track by $index" ng-if="beer.style"> 
     <!-- ng-if will make sure there is some information being displayed 
      for each beer --> 
     <h2>{{beer.name}}</h2> 
     <h3>{{beer.style.name}}</h3> 
     <p>AbvMin: {{beer.abv}}</p> 
     <p>AbvMax: {{beer.ibu}}</p>  
     <p>{{beer.style.description}}</p> 
     <hr> 
     </div> 
    </div> 
    </div> 

    <div ng-if="beers.status==='failure'"> 
    <p>No results found.</p> 
    </div> 
</body> 
Problemi correlati