web-dev-qa-db-fra.com

AngularJS Scope non mis à jour après un appel asynchrone

Je ne parviens pas à mettre à jour mon étendue sur l'interface frontale lors d'une requête auprès d'une API. Sur le backend, je peux voir que la valeur de ma variable $ scope est en train de changer, mais cela ne se reflète pas dans les vues.

Voici mon contrôleur.

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);
      });
    });  
 }
});

Voici un extrait de mon code 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>

J'ai essayé plusieurs solutions, notamment en utilisant $ scope. $ Apply (); mais cela ne fait que créer l'erreur commune

Erreur: $ digest déjà en cours

Le message suivant suggérait d'utiliser $ timeout ou $ asyncDefault AngularJS: Empêcher l'erreur $ digest déjà en cours lors de l'appel de $ scope. $ Apply ()

Le code que j'ai ci-dessus utilise $ timeout et je n'ai aucune erreur mais la vue n'est toujours pas mise à jour.

Aide appréciée

13
peterpod

Si vous utilisez AngularJS 1.3+, vous pouvez essayer $scope.$applyAsync() juste après l’instruction $scope.beers = response.data;.

C’est ce que dit la documentation de Angular à propos de $applyAsync()

Planifiez l'invocation de $ apply pour qu'elle se produise ultérieurement. La différence de temps réelle varie d’un navigateur à l’autre, mais elle est généralement d’environ 10 millisecondes. La source

Mettre à jour

Comme d'autres l'ont souligné, vous ne devriez pas (généralement) avoir besoin de déclencher le cycle de digestion manuellement. La plupart du temps, cela ne fait que signaler une mauvaise conception (ou du moins pas une conception conviviale pour AngularJS) de votre application.

Actuellement dans l'OP, la méthode fetch est déclenchée sur $ watch. Si au lieu de cela, cette méthode devait être déclenchée par ngChange, le cycle de résumé devrait être déclenché automatiquement.

Voici un exemple à quoi un tel code pourrait ressembler:

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);
    };

}
11
Vladimir Zdenek

Hé les gars, j'ai résolu le problème mais je ne sais pas exactement pourquoi cela a changé quoi que ce soit. Réorganisation de mon code sur JS Fiddle Je viens de placer tous mes partiels dans le fichier index.html, comme ceci, et les requêtes et les variables de portée ont été mises à jour en douceur. Y at-il peut-être un conflit de contrôleur avec mon HTML ci-dessus?

<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>
0
peterpod

Comme le suggèrent les commentaires, vous ne devriez pas avoir besoin d'utiliser $timeout pour déclencher un cycle de résumé. Tant que l'UX qui provoque le changement se trouve dans limite d'une construction angulaire (par exemple, fonction du contrôleur, service, etc.), elle doit se manifester dans le cycle de résumé.

D'après ce que je peux déduire de votre message, vous utilisez probablement une entrée de recherche pour atteindre une API avec des résultats. Je vous conseillerais de modifier la logique de manière à déclencher votre recherche sur un événement explicite plutôt que sur le $watcher.

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

Supprimez la logique $watch et le wrapper $timeout.

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;  
});
}

Les raisons pour lesquelles je fais cette recommandation sont:

  • Vous avez un contrôle plus précis sur la façon dont le rappel ng-change est déclenché à l'aide de ng-model-options. Cela signifie que vous pouvez y mettre un délai, vous pouvez déclencher différents événements UX, etc.
  • Vous avez conservé une séquence plus claire de la façon dont fetch est appelé.
  • Vous avez peut-être évité les problèmes de performances et $ digest.
0
jusopi