서비스에서 $ http 응답 처리


233

최근에 내가 여기 에서 직면하고있는 문제에 대한 자세한 설명을 게시했습니다 . 실제 $http요청을 보낼 수 없으므로 시간 초과를 사용하여 비동기 동작을 시뮬레이션했습니다. @Gloopy의 도움으로 모델에서보기로 데이터 바인딩이 올바르게 작동합니다.

이제 (로컬에서 테스트 됨) $http대신 사용 $timeout하면 비동기 요청이 성공했으며 data서비스에서 json 응답으로 채워졌습니다. 그러나 내 견해는 업데이트되지 않습니다.

Plunkr 업데이트 여기

답변:


419

원하는 것을 수행하는 Plunk는 다음과 같습니다. 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;
    });
  };
});

13
서비스가 인터셉트 된 후에도 컨트롤러에서 성공 및 오류 메소드를 호출 할 수있는 방법이 then있습니까?
andyczerwonka

2
@PeteBD myService.async()다양한 컨트롤러에서 여러 번 호출 $http.get()하려면 첫 번째 요청에 대해서만 서비스를 구성 하고 모든 후속 요청은 첫 번째 호출에서 설정된 로컬 객체 배열을 반환합니다 myService.async(). 즉, 실제로 하나만 만들면 JSON 서비스에 대한 여러 개의 불필요 한 요청을 피하고 싶습니다.
GFoley83

5
@ GFoley83-여기 있습니다 : plnkr.co/edit/2yH1F4IMZlMS8QsV9rHv?p=preview . 콘솔을 보면 요청이 한 번만 이루어진 것을 볼 수 있습니다.
피트 BD

3
@PeteBD $scope.data = myService.async()컨트롤러에서 직접 사용할 수도 있다고 생각합니다 .
Julian

2
@ Blowsie- Plunks를 업데이트했습니다. 다음은 원본입니다 (1.2RC3으로 업데이트). plnkr.co/edit/3Nwxxk?p=preview 다음은 서비스를 사용하는 것입니다 : plnkr.co/edit/a993Mn?p=preview
Pete BD

82

간단하게하자. 그것은 간단합니다

  1. promise서비스 반품 (서비스에서 사용할 필요 없음 then)
  2. 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;
  });
});

귀하의 링크에서 app.factory, 그리고 귀하의 코드에서 app.service입니다. app.factory이 경우에 가정 합니다.
Re Captcha

1
app.service도 작동합니다. 또한-이것은 나에게 가장 우아한 솔루션처럼 보입니다. 뭔가 빠졌습니까?
user1679130

1
나는 각도 문제가있을 때마다 @allenhwkim에 답이있는 것처럼 보입니다! (이번 주 NG 맵 구성 요소 btw에서 세 번째)
Yarin

난 그냥 status_code와 함께 여기에 성공과 오류를 넣는 방법을 알고 싶습니다
Anuj

58

비동기식이므로 $scopeajax 호출이 완료되기 전에 데이터를 가져옵니다.

당신은 사용할 수 있습니다 $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();
  });
});

2
+1 다른 것보다 OO가 많기 때문에이 것이 가장 좋습니다. 그러나이하지 않는 이유가 this.async = function() {this.getData = function() {return data}? 나는 당신이 무슨 뜻인지 알기를 바랍니다
자전거

@bicycle 나는 같은 방식으로 원했지만 약속을 완전히 해결해야하기 때문에 작동하지 않습니다. 평소처럼 액세스하지 않고 액세스하려고하면 내부 데이터에 액세스 할 때 참조 오류가 발생합니다. 말이 되길 바래?
user6123723

만약 내가 제대로 이해하고 그것을 추가 할 필요가 deffered = $q.defer()나는 myService.async () 두 개 이상의 시간을 호출 할 경우 myService.async 내부
데마

1
이 예제는 고전적인 지연된 안티 패턴 입니다. 와 약속을 제조 할 필요가 없다 $q.defer는 AS $http서비스가 이미 약속을 돌려줍니다. $http오류가 반환 되면 반환 된 약속이 중단됩니다 . 또한 .successand .error메소드는 더 이상 사용되지 않으며 AngularJS 1.6에서 제거 되었습니다 .
georgeawg 2016

23

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

});

커피 스크립트의 작은 데모 : http://plunker.no.de/edit/ksnErx?live=preview

귀하의 plunker가 내 방법으로 업데이트되었습니다 : http://plnkr.co/edit/mwSZGK?p=preview


나는 당신의 접근 방식에 따라 더 시도 할 것입니다. 그러나 결과를 반환하는 대신 서비스로 캡처하고 싶습니다. 여기에 관련된 질문을 참조하십시오 stackoverflow.com/questions/12504747/...~~V을 . 컨트롤러에서 다른 방법으로 $ http로 반환 된 데이터를 처리하고 싶습니다. 도와 주셔서 감사합니다.
bsr September

서비스에서 약속을 사용할 수 있습니다. $ watch가 마음에 들지 않으면”promise.then (function (data) {service.data = data;}, onErrorCallback);`
Guillaume86

나는 너의 포크를 추가했다
Guillaume86

1
또는 서비스에서 $ scope. $ emit을 사용하고 ctrl에서 $ scope. $ on을 사용하여 컨트롤러에 데이터가 반환되었지만 실제로 이점을 얻지 못한다고 말할 수 있습니다.
Guillaume86

7

내가 생각하는 훨씬 더 좋은 방법은 다음과 같습니다.

서비스:

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

Angular는 자동으로 해결 된 것을에 awesomeFruits넣습니다 $scope.fruits.


4
deferred.resolve ()? 보다 정확하게 제발, 그리고 $ http 전화는 어디에 있습니까? 또한 왜 .service에서 객체를 반환합니까?

6

나는 같은 문제가 있었지만 인터넷에서 서핑을 할 때 $ http가 기본적으로 약속을 되찾았다는 것을 이해했다. 그리고 나서 "data"를 반환 한 후 "then"으로 사용할 수있다. 코드를보십시오 :

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

4

UI를 배열에 바인딩 할 때 길이를 0으로 설정하고 데이터를 배열로 푸시하여 동일한 배열을 직접 업데이트해야합니다.

이 대신 ( dataUI가 알지 못하는 다른 배열 참조를 설정 ) :

 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을 작동시킬 수 없었지만 희망적으로 이것은 당신을 위해 작동합니다!


작동하지 않았다. 콘솔 로그에서 d는 성공 콜백에서 올바르게 업데이트되지만 데이터는 업데이트되지 않는 것을 볼 수 있습니다. 기능이 이미 실행되었을 수 있습니다.
bsr September

이 방법은 확실히 작동해야합니다 .d가 아닌 데이터 유형이 배열이 아닌 dsp와 관련이있을 수 있습니다 (예 : asp.net에서는 배열에 대해 dd에 액세스해야합니다). 오류 발생시 문자열을 배열로 푸시하는 예는 plnkr을 참조하십시오. plnkr.co/edit/7FuwlN?p=preview
Gloopy

1
angular.copy(d, data)작동합니다. 대상이 copy () 메소드에 제공되면 대상의 요소를 먼저 삭제 한 다음 소스에서 새 요소를 복사합니다.
Mark Rajcok

4

이와 관련하여 나는 비슷한 문제를 겪었지만 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);
    });
});

이것이 다른 사람들이 같은 문제를 얻는 데 도움이되기를 바랍니다.


4

내가 읽은 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();
            });

이 도움을 바랍니다


작동하지 않습니다. 의 반환 값은 defrred.promise함수가 아닙니다.
Jürgen Paul

@PineappleUndertheSea 왜 기능이 필요합니까? 그것은 약속의 대상입니다.
Chev

@PineappleUndertheSea 당신은 연기되고 거부되지 않는 것을 사용하려고 했습니까?
데릭

2
PeteBD가 지적한 바와 같이,이 양식은 $scope.items = Data.getData(); Anglular에서 더 이상 사용되지 않습니다
호화 로울지도 모르는

2

나는 "약속적인"방식으로 일을하기 때문에 $ 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의 직관적이거나 일반적인 패턴은 아닙니다.
  • getDataobj많은 응용 프로그램에서 문제가되지는 않지만 객체 형식의 매개 변수 만 허용 할 수는 있지만 배열은 허용 할 수 있지만 아픈 제한입니다.
  • 입력 객체 $scope.data= {}객체 (본질적으로 $scope.clearData()위의 것)로 만들거나 = []배열로 만들거나 작동하지 않습니다 (우리는 이미 어떤 데이터가오고 있는지에 대해 뭔가를 가정해야합니다). 이 준비 단계 IN을 시도 getData했지만 운이 없습니다.

그럼에도 불구하고, 컨트롤러 "포장 풀기"상용구를 제거하는 패턴을 제공하며 DRY를 유지하면서 $ http에서 얻은 특정 데이터를 여러 곳에서 사용하려는 경우에 유용 할 수 있습니다.


1

서비스 응답을 캐싱하는 한, 여기에 내가 본 것보다 더 직설적으로 보이는 또 다른 버전이 있습니다.

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

0

아래 코드를 시도하십시오

컨트롤러 (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;
             });
         }
    }
}());

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.