Обработка ответа $http в службе
недавно я опубликовал подробное описание проблемы, с которой я сталкиваюсь здесь, в SO. Как я не мог отправить реальный $http
запрос, я использовал тайм-аут для моделирования асинхронного поведения. Привязка данных из моей модели для просмотра работает правильно, с помощью @Gloopy
теперь, когда я использую $http
вместо $timeout
(проверено локально), я мог видеть, что асинхронный запрос был успешным и data
заполняется ответом json в моем сервисе. Но, на мой взгляд, нет обновление.
обновление Plunkr здесь
12 ответов:
вот Планк, который делает то, что вы хотите:http://plnkr.co/edit/TTlbSv?p=preview
идея заключается в том, что вы работаете с обещаниями напрямую и их функциями "затем" для управления и доступа к асинхронно возвращаемым ответам.
app.factory('myService', function($http) { var myService = { async: function() { // $http returns a promise, which has a then function, which also returns a promise var promise = $http.get('test.json').then(function (response) { // The then function here is an opportunity to modify the response console.log(response); // The return value gets picked up by the then in the controller. return response.data; }); // Return the promise to the controller return promise; } }; return myService; }); app.controller('MainCtrl', function( myService,$scope) { // Call the async method and then do stuff with what is returned inside our own then function myService.async().then(function(d) { $scope.data = d; }); });
вот немного более сложная версия, которая кэширует запрос, так что вы только сделать это в первый раз (http://plnkr.co/edit/2yH1F4IMZlMS8QsV9rHv?p=preview):
app.factory('myService', function($http) { var promise; var myService = { async: function() { if ( !promise ) { // $http returns a promise, which has a then function, which also returns a promise promise = $http.get('test.json').then(function (response) { // The then function here is an opportunity to modify the response console.log(response); // The return value gets picked up by the then in the controller. return response.data; }); } // Return the promise to the controller return promise; } }; return myService; }); app.controller('MainCtrl', function( myService,$scope) { $scope.clearData = function() { $scope.data = {}; }; $scope.getData = function() { // Call the async method and then do stuff with what is returned inside our own then function myService.async().then(function(d) { $scope.data = d; }); }; });
пусть это будет простой. Это так же просто, как
- возвращение
promise
в вашем сервисе(не нужно использоватьthen
в сервис)- использовать
then
в контроллередемо. http://plnkr.co/edit/cbdG5p?p=preview
var app = angular.module('plunker', []); app.factory('myService', function($http) { return { async: function() { return $http.get('test.json'); //1. this returns promise } }; }); app.controller('MainCtrl', function( myService,$scope) { myService.async().then(function(d) { //2. so you can use .then() $scope.data = d; }); });
потому что это асинхронно,
$scope
получает данные до завершения вызова ajax.вы могли бы использовать
$q
в вашем сервисе для созданияpromise
и вернуть его контроллер, и контроллер получить результат в пределахthen()
вызов в отношенииpromise
.в службе
app.factory('myService', function($http, $q) { var deffered = $q.defer(); var data = []; var myService = {}; myService.async = function() { $http.get('test.json') .success(function (d) { data = d; console.log(d); deffered.resolve(); }); return deffered.promise; }; myService.data = function() { return data; }; return myService; });
затем, в вашем контроллере:
app.controller('MainCtrl', function( myService,$scope) { myService.async().then(function() { $scope.data = myService.data(); }); });
у tosh shimayama есть решение, но вы можете многое упростить, если используете тот факт, что $http возвращает обещания и что обещания могут возвращать значение:
app.factory('myService', function($http, $q) { myService.async = function() { return $http.get('test.json') .then(function (response) { var data = reponse.data; console.log(data); return data; }); }; return myService; }); app.controller('MainCtrl', function( myService,$scope) { $scope.asyncData = myService.async(); $scope.$watch('asyncData', function(asyncData) { if(angular.isDefined(asyncData)) { // Do something with the returned data, angular handle promises fine, you don't have to reassign the value to the scope if you just want to use it with angular directives } }); });
небольшая демонстрация в coffeescript:http://plunker.no.de/edit/ksnErx?live=preview
ваш планкер обновлен с моим методом:http://plnkr.co/edit/mwSZGK?p=preview
гораздо лучше, я думаю, будет что-то вроде этого:
сервис:
app.service('FruitsManager',function($q){ function getAllFruits(){ var deferred = $q.defer(); ... // somewhere here use: deferred.resolve(awesomeFruits); ... return deferred.promise; } return{ getAllFruits:getAllFruits } });
а в контроллере вы можете просто использовать:
$scope.fruits = FruitsManager.getAllFruits();
угловой автоматически поставит разрешенный
awesomeFruits
на$scope.fruits
.
У меня была та же проблема, но когда я занимался серфингом в Интернете, я понял, что $http возвращает по умолчанию обещание, тогда я мог бы использовать его с "затем" после возврата "данных". посмотрите на код:
app.service('myService', function($http) { this.getData = function(){ var myResponseData = $http.get('test.json').then(function (response) { console.log(response);. return response.data; }); return myResponseData; } }); app.controller('MainCtrl', function( myService, $scope) { // Call the getData and set the response "data" in your scope. myService.getData.then(function(myReponseData) { $scope.data = myReponseData; }); });
при привязке пользовательского интерфейса к массиву вы хотите, чтобы убедиться, что вы обновить тот же массив непосредственно, установив длину 0 и нажав данные в массив.
вместо этого (который устанавливает другую ссылку на массив
data
о котором ваш пользовательский интерфейс не будет знать):myService.async = function() { $http.get('test.json') .success(function (d) { data = d; }); };
попробуйте это:
myService.async = function() { $http.get('test.json') .success(function (d) { data.length = 0; for(var i = 0; i < d.length; i++){ data.push(d[i]); } }); };
вот скрипка это показывает разницу между установкой нового массива против опорожнения и добавлением к существующему. Я не мог получить ваш plnkr работает, но, надеюсь, это работает для вас!
В связи с этим я прошел через аналогичную проблему, но не с get или post, сделанным Angular, а с расширением, сделанным третьей стороной (в моем случае расширение Chrome).
Проблема, что я столкнулся заключается в том, что расширение Chrome не вернетсяthen()
поэтому я не смог сделать это так, как в решении выше, но результат по-прежнему асинхронный.
Поэтому мое решение-создать сервис и перейти к обратному вызовуapp.service('cookieInfoService', function() { this.getInfo = function(callback) { var model = {}; chrome.cookies.get({url:serverUrl, name:'userId'}, function (response) { model.response= response; callback(model); }); }; });
тогда в моем контроллер
app.controller("MyCtrl", function ($scope, cookieInfoService) { cookieInfoService.getInfo(function (info) { console.log(info); }); });
надеюсь, что это может помочь другим получить ту же проблему.
Я читал http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/ [AngularJS позволяет нам оптимизировать нашу логику контроллера, помещая обещание непосредственно в область, а не вручную передавать разрешенное значение в успешном обратном вызове.]
так просто и удобно :)
var app = angular.module('myApp', []); app.factory('Data', function($http,$q) { return { getData : function(){ var deferred = $q.defer(); var promise = $http.get('./largeLoad').success(function (response) { deferred.resolve(response); }); // Return the promise to the controller return deferred.promise; } } }); app.controller('FetchCtrl',function($scope,Data){ $scope.items = Data.getData(); });
надеюсь, что это поможет
мне действительно не нравится тот факт, что из-за "обещания" способ делать вещи, потребитель сервиса, который использует $http должен "знать" о том, как распаковать ответ.
я просто хочу позвонить что-то и получить данные, похожие на старые
$scope.items = Data.getData();
путь, который теперь устаревший.я пытался некоторое время и не придумал идеальное решение, но вот мой лучший выстрел (Plunker). Это может быть полезно кто-то.
контроллер:app.factory('myService', function($http) { var _data; // cache data rather than promise var myService = {}; myService.getData = function(obj) { if(!_data) { $http.get('test.json').then(function(result){ _data = result.data; console.log(_data); // prove that it executes once angular.extend(obj, _data); }); } else { angular.extend(obj, _data); } }; return myService; });
app.controller('MainCtrl', function( myService,$scope) { $scope.clearData = function() { $scope.data = Object.create(null); }; $scope.getData = function() { $scope.clearData(); // also important: need to prepare input to getData as an object myService.getData($scope.data); // **important bit** pass in object you want to augment }; });
недостатки я уже могу определить
- вы должны передать в объект, который вы хотите данные добавил, который не является интуитивным или общим шаблоном в Angular
getData
можно только принятьobj
параметр в виде объекта (хотя он также может принимать массив), который не будет проблемой для многих приложений, но это боль ограничение- вы должны подготовить объект ввода
$scope.data
С= {}
чтобы сделать его объектом (по сути то, что$scope.clearData()
выше), или= []
для массива, или он не будет работать (мы уже должны предположить что-то о том, какие данные поступают). Я попытался сделать этот шаг подготовки вgetData
, но не повезло.тем не менее, он предоставляет шаблон, который удаляет контроллер "promise unwrap" шаблонный, и может быть полезен в тех случаях, когда вы хотите использовать некоторые данные, полученные из $http в более чем одном месте, сохраняя его сухим.
что касается кэширования ответа в Службе, вот еще одна версия, которая кажется более прямой, чем то, что я видел до сих пор:
App.factory('dataStorage', function($http) { var dataStorage;//storage for cache return (function() { // if dataStorage exists returned cached version return dataStorage = dataStorage || $http({ url: 'your.json', method: 'GET', cache: true }).then(function (response) { console.log('if storage don\'t exist : ' + response); return response; }); })(); });
эта служба вернет либо кэшированные данные, либо
$http.get
;dataStorage.then(function(data) { $scope.data = data; },function(e){ console.log('err: ' + e); });
пожалуйста, попробуйте ниже код
вы можете разделить контроллер (PageCtrl) и службы (dataService)
'use strict'; (function () { angular.module('myApp') .controller('pageContl', ['$scope', 'dataService', PageContl]) .service('dataService', ['$q', '$http', DataService]); function DataService($q, $http){ this.$q = $q; this.$http = $http; //... blob blob } DataService.prototype = { getSearchData: function () { var deferred = this.$q.defer(); //initiating promise this.$http({ method: 'POST',//GET url: 'test.json', headers: { 'Content-Type': 'application/json' } }).then(function(result) { deferred.resolve(result.data); },function (error) { deferred.reject(error); }); return deferred.promise; }, getABCDATA: function () { } }; function PageContl($scope, dataService) { this.$scope = $scope; this.dataService = dataService; //injecting service Dependency in ctrl this.pageData = {}; //or []; } PageContl.prototype = { searchData: function () { var self = this; //we can't access 'this' of parent fn from callback or inner function, that's why assigning in temp variable this.dataService.getSearchData().then(function (data) { self.searchData = data; }); } } }());