सेवा में $ http प्रतिक्रिया संसाधित करना


233

मैंने हाल ही में एसओ पर यहां जारी समस्या का विस्तृत विवरण पोस्ट किया है । जैसा कि मैं एक वास्तविक $httpअनुरोध नहीं भेज सका , मैंने अतुल्यकालिक व्यवहार का अनुकरण करने के लिए समय-समय पर उपयोग किया। मेरे मॉडल से देखने के लिए डेटा बाइंडिंग सही काम कर रही है, @Gloopy की मदद से

अब, जब मैं (स्थानीय रूप से परीक्षण किए गए) के $httpबजाय उपयोग करता हूं, तो मैं $timeoutदेख सकता था कि एसिंक्रोनस अनुरोध सफल था और dataमेरी सेवा में json प्रतिक्रिया से भरा है। लेकिन, मेरा विचार अपडेट नहीं हो रहा है।

यहां अपडेट किया गया प्लंकर

जवाबों:


419

यहां एक प्लंक है जो आप चाहते हैं: http://plnkr.co/edit/TTlbSv?p=nview

विचार यह है कि आप सीधे वादों के साथ काम करते हैं और उनके "फिर" कार्यों में अतुल्यकालिक रूप से वापस आने वाली प्रतिक्रियाओं में हेरफेर करने और उन तक पहुंचने का कार्य होता है।

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?
एंडाइज़रवोनका

2
@PeteBD अगर मैं अपने myService.async()कई बार विभिन्न नियंत्रकों से कॉल करना चाहता हूं , तो आप सेवा को कैसे व्यवस्थित करेंगे, ताकि यह केवल $http.get()पहले अनुरोध के लिए ही हो, और बाद के सभी अनुरोध स्थानीय ऑब्जेक्ट सरणी को लौटाएं जो पहली कॉल पर सेट हो जाता है myService.async()। दूसरे शब्दों में, मैं JSON सेवा के लिए कई, अनावश्यक अनुरोधों से बचना चाहता हूं, जब वास्तव में मुझे केवल एक बनाने की आवश्यकता होती है।
GFoley83

5
@ GFoley83 - यहां आप जाएं: plnkr.co/edit/2yH1F4IMZlMS8QsV9rHv?p=preview । यदि आप कंसोल को देखते हैं तो आप देखेंगे कि अनुरोध केवल एक बार किया गया है।
पीट बीडी

3
@PeteBD मुझे लगता है कि आप $scope.data = myService.async()नियंत्रक में भी सीधे उपयोग कर सकते हैं ।
जूलियन

2
@ Blowsie- मैंने प्लॉक्स को अपडेट किया है। यहां मूल (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इस मामले में माना जाता है।
रे कैप्चा

1
app.service भी काम करते हैं। इसके अलावा - यह मेरे लिए सबसे सुरुचिपूर्ण समाधान की तरह दिखता है। क्या मैं कुछ भूल रहा हूँ?
user1679130

1
हर बार लगता है कि मुझे एक कोणीय समस्या है @allenhwkim का जवाब! (इस सप्ताह 3 बार- महान एनजी-मैप घटक btw)
यारिन

मैं बस यहीं स्टेटस_कोड के साथ सफलता और त्रुटि को जानना चाहता हूं
अनुज

58

क्योंकि यह एसिंक्रोनस है, $scopeअजाक्स कॉल पूरा होने से पहले डेटा प्राप्त कर रहा है।

आप $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}? मुझे आशा है कि आपको मेरा क्या मतलब है
साइकिल

@ साइकल मैं इसे वैसे ही चाहता था लेकिन यह काम नहीं करेगा क्योंकि वादे को सभी तरह से हल करना होगा। यदि आप ऐसा नहीं करते हैं और सामान्य रूप से इसे एक्सेस करने का प्रयास करते हैं, तो आंतरिक डेटा एक्सेस करते समय आपको एक संदर्भ त्रुटि मिलेगी। आशा है कि यह समझ में आता है?
user6123723

अगर मैं सही ढंग से समझता deffered = $q.defer()हूं कि myService.async के अंदर जोड़ना आवश्यक है , अगर मैं myService.async () को दो या अधिक समय पर कॉल करना चाहता हूं
demas

1
यह उदाहरण एक क्लासिक आस्थगित विरोधी पैटर्न है । वादे के निर्माण की कोई आवश्यकता नहीं है $q.deferक्योंकि $httpसेवा पहले से ही एक वादा वापस करती है। यदि $httpरिटर्न में त्रुटि हुई तो वादा पूरा हो जाएगा । इसके अलावा .successऔर .errorतरीकों को पदावनत किया जाता है और उन्हें AngularJS 1.6 से हटा दिया जाता है
georgeawg

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?ln=hi

आपका प्लंकर मेरी विधि से अपडेट किया गया: http://plnkr.co/edit/mwSZGK?p=preview


मैं आपके दृष्टिकोण के साथ आगे की कोशिश करूँगा। लेकिन, मुझे लौटने के बजाय सेवा में परिणाम पर कब्जा करना पसंद है। इससे संबंधित प्रश्न यहाँ देखें stackoverflow.com/questions/12504747/… । मैं नियंत्रक में अलग-अलग तरीकों से $ http द्वारा लौटाए गए डेटा को संसाधित करना पसंद करता हूं। आपकी सहायता के लिए एक बार फिर से धन्यवाद।
bsr

आप सेवाओं में वादों का उपयोग कर सकते हैं, अगर आपको $ घड़ी पसंद नहीं है, तो आप rompromise.then (फ़ंक्शन (डेटा) {service.data = data;}, onErrorCallback) कर सकते हैं; `
Guillaume86

मैं एक plunker तुम्हारा से अलग जोड़ा
Guillaume86

1
वैकल्पिक रूप से आप $ गुंजाइश का उपयोग कर सकते हैं। सेवा से $ emit और $ गुंजाइश। $ ctrl पर $ आपको नियंत्रक को यह बताने के लिए कि डेटा वापस आ गया है, लेकिन मुझे वास्तव में कोई लाभ नहीं दिखता है
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();

कोणीय स्वचालित रूप से हल awesomeFruitsमें डाल देगा $scope.fruits


4
deferred.resolve ()? कृपया अधिक सटीक रहें और $ http कॉल कहां है? इसके अलावा, आप एक वस्तु को सेवा में क्यों लौटाते हैं?

6

मुझे भी यही समस्या थी, लेकिन जब मैं इंटरनेट पर सर्फिंग कर रहा था तो मैं समझ गया था कि $ 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;
      });
 });

4

यूआई को अपने सरणी से बाँधते समय आप यह सुनिश्चित करना चाहेंगे कि आप उसी सरणी को सीधे 0 पर लंबाई निर्धारित करके और डेटा को सरणी में धकेल कर अपडेट करें।

इसके बजाय (जो एक अलग सरणी संदर्भ सेट करता है dataजिसके बारे में आपके UI को पता नहीं होगा):

 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 काम नहीं कर सका, लेकिन उम्मीद है कि यह आपके लिए काम करता है!


यह काम नहीं किया। कंसोल लॉग में, मैं देख सकता हूं कि डी को सफलता कॉलबैक में ठीक से अपडेट किया गया है, लेकिन डेटा नहीं। हो सकता है कि फ़ंक्शन पहले से ही निष्पादित हो।
bsr

इस विधि को निश्चित रूप से काम करना चाहिए हो सकता है कि यह डेटा प्रकार के साथ कुछ करने में सक्षम न हो एक सरणी (asp.net में आपको उदाहरण के लिए सरणी के लिए dd तक पहुंचने की आवश्यकता होगी)। उदाहरण के लिए इस plnkr को त्रुटि पर एक स्ट्रिंग में धकेलने वाले उदाहरण के लिए देखें: plnkr.co/edit/7FuwlN?p=preview
ग्लॉपी

1
angular.copy(d, data)भी चलेगा। जब किसी गंतव्य को कॉपी () विधि से आपूर्ति की जाती है, तो वह पहले गंतव्य के तत्वों को हटा देगा, और फिर स्रोत से नए लोगों में कॉपी करेगा।
मार्क राजकॉक

4

इससे संबंधित मैं एक ऐसी ही समस्या से गुज़रा, लेकिन एंगुलर द्वारा प्राप्त या पोस्ट के साथ नहीं, बल्कि एक 3rd पार्टी द्वारा किए गए एक्सटेंशन के साथ (मेरे मामले में 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/use-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एक फ़ंक्शन नहीं है।
जुरगेन पॉल

@PineappleUndertheSea को एक फ़ंक्शन की आवश्यकता क्यों है? यह एक वादा वस्तु है।
Chev

@PineappleUndertheSea का मतलब क्या आप स्थगित और अवहेलना नहीं करते?
डेरिक

2
PeteBD के रूप में कहे अनुसार, इस प्रपत्र $scope.items = Data.getData(); Anglular में पदावनत किया गया है
poshest

2

मैं वास्तव में इस तथ्य को पसंद नहीं करता कि, चीजों को करने के "वादे" तरीके के कारण, प्रतिक्रिया को अनपैक करने के तरीके के बारे में $ http का उपयोग करने वाले सेवा के उपभोक्ता को "पता" है।

मैं केवल कुछ कॉल करना चाहता हूं और पुराने $scope.items = Data.getData();तरीके के समान डेटा को बाहर निकालना चाहता हूं , जिसे अब हटा दिया गया है

मैंने कुछ समय तक कोशिश की और एक सही समाधान के साथ नहीं आया, लेकिन यहां मेरा सबसे अच्छा शॉट ( प्लंकर ) है। यह किसी के लिए उपयोगी हो सकता है।

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

पंजे मैं पहले से ही हाजिर हैं

  • आपको उस ऑब्जेक्ट में पास करना होगा जो आप चाहते हैं कि डेटा जोड़ा जाए , जो कि कोणीय में एक सहज या सामान्य पैटर्न नहीं है
  • getDataकेवल objऑब्जेक्ट के रूप में पैरामीटर को स्वीकार कर सकते हैं (हालांकि यह एक सरणी को भी स्वीकार कर सकता है), जो कई अनुप्रयोगों के लिए समस्या नहीं होगी, लेकिन यह एक गंभीर सीमा है
  • आप इनपुट वस्तु तैयार करना है $scope.dataके साथ = {}यह एक वस्तु बनाने के लिए (अनिवार्य रूप से क्या $scope.clearData()ऊपर करता है), या = []एक सरणी के लिए है, या यह काम नहीं (हम पहले से ही क्या डेटा आ रहा है के बारे में कुछ ग्रहण करने के लिए आ रही हैं,) होगा। मैंने इस तैयारी को IN में करने की कोशिश की getData, लेकिन कोई किस्मत नहीं।

फिर भी, यह एक पैटर्न प्रदान करता है जो नियंत्रक "वादा अनट्रैप" बॉयलरप्लेट को हटा देता है, और ऐसे मामलों में उपयोगी हो सकता है जब आप $ DR से प्राप्त कुछ डेटा का उपयोग DRY रखते हुए एक से अधिक स्थानों पर करना चाहते हैं।


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) और सेवा (डेटा सेवा) को विभाजित कर सकते हैं

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.