Область AngularJS не обновляется после асинхронного вызова
У меня возникли проблемы с обновлением моей области видимости в интерфейсе, когда вы обращаетесь к API. На бэкэнде я вижу, что значение переменной my $ scope меняется, но это не отражается в представлениях.
Вот мой контроллер.
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);
});
});
}
});
Вот фрагмент моего 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>
Я попробовал несколько решений, включая использование $ scope. $ Apply(); но это просто создает общую ошибку
Ошибка: $ digest уже выполняется
Следующая публикация предложила использовать $ timeout или $ asyncDefault AngularJS: Предотвратить ошибку $ digest, которая уже выполняется при вызове $ scope. $ Apply()
Код, который у меня выше, использует $ timeout, и у меня нет ошибок, но все же представление не обновляется.
Помогите оценить
Ответы
Ответ 1
Я использую AngularJS 1. 3+, вы можете попробовать $scope.$applyAsync()
сразу после $scope.beers = response.data;
выражение.
Это то, что Angular documentation говорит о $applyAsync()
Запланируйте вызов $ apply, чтобы произойти в более позднее время. Фактическая разница во времени варьируется в разных браузерах, но обычно около ~ 10 миллисекунд. Источник
Обновить
Как указывали другие, вы не должны (обычно) запускать цикл дайджеста вручную. В большинстве случаев это просто указывает на плохой дизайн (или, по крайней мере, не дизайн, ориентированный на AngularJS) вашего приложения.
В настоящее время в OP метод fetch
запускается на $ watch. Если вместо этого метод должен был быть вызван ngChange
, цикл дайджеста должен запускаться автоматически.
Вот пример того, как выглядит такой код:
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);
};
}
Ответ 2
Как следует из комментариев, вам не нужно использовать $timeout
для запуска цикла дайджеста. Пока UX, который вызывает изменение, находится в пределах угловой конструкции (например, функции контроллера, службы и т.д.), Тогда он должен проявляться в цикле дайджеста.
Основываясь на том, что я могу сделать из вашего сообщения, вы, вероятно, используете вход для поиска, чтобы попасть в API с результатами. Я бы рекомендовал изменить логику таким образом, чтобы вы запускали поиск по явному событию, а не к $watch
er.
<input ng-model="search" ng-change="fetch()">
Удалите логику $watch
и $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 a good habit to return your data in the promise APIs
return $scope.beers;
});
}
Причины, по которым я рекомендую эту рекомендацию:
- У вас есть более тонкий контроль за тем, как вызван обратный вызов
ng-change
с помощью ng-model-options
. Это означает, что вы можете наложить на него задержку, вы можете запускать различные события UX и т.д. - Вы сохранили более четкую последовательность того, как вызывается
fetch
. - Возможно, вы избежали проблем с производительностью и $ digest.
Ответ 3
Эй, ребята, я решил проблему, но я точно не знаю, почему это ничего не изменило. Переупорядочивая мой код на JS Fiddle, я просто поместил все мои частичные файлы в файл index.html, как и переменные запроса и области видимости, которые плавно обновляются. Может быть, конфликт контроллера с моим html выше?
<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>