Serviste $ http yanıtı işleniyor


233

Geçenlerde burada SO ile karşı karşıya olduğum sorunun ayrıntılı bir açıklamasını gönderdim . Gerçek bir $httpistek gönderemediğim için zaman uyumsuzluğu eşzamansız davranışı simüle etmek için kullandım. Modelimden görüntülemek için veri bağlama @Gloopy yardımıyla doğru çalışıyor

Şimdi, (yerel olarak test edilmiş) $httpyerine kullandığımda $timeout, eşzamansız isteğin başarılı olduğunu ve datahizmetimde json yanıtıyla dolu olduğunu görebiliyordum. Ancak benim görüşüm güncellenmiyor.

Plunkr burada güncellendi

Yanıtlar:


419

İşte istediğinizi yapan bir Plunk: http://plnkr.co/edit/TTlbSv?p=preview

Fikir, doğrudan vaatlerle ve eşzamansız olarak döndürülen yanıtları işlemek ve erişmek için "o zaman" işlevleriyle çalışmanızdır.

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

İşte isteği önbelleğe alan biraz daha karmaşık bir sürüm, bu yüzden yalnızca ilk kez ( 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
Hizmet durdurulduktan sonra denetleyicideki başarı ve hata yöntemlerini çağırmanın bir yolu var mı then?
andyczerwonka

2
@PeteBD myService.async()Çeşitli denetleyicilerden birden çok kez çağırmak istersem , hizmeti yalnızca $http.get()ilk istekte olduğu şekilde nasıl düzenlersiniz ve sonraki tüm istekler yalnızca ilk çağrıda ayarlanan yerel bir nesne dizisini döndürür myService.async(). Başka bir deyişle, gerçekten sadece bir tane yapmak gerektiğinde JSON hizmetine birden fazla, gereksiz isteklerden kaçınmak istiyorum.
GFoley83

5
@ GFoley83 - İşte başlıyoruz : plnkr.co/edit/2yH1F4IMZlMS8QsV9rHv?p=preview . Konsola bakarsanız, isteğin yalnızca bir kez yapıldığını görürsünüz.
Pete BD

3
@PeteBD $scope.data = myService.async()Doğrudan kumandada da kullanabileceğinizi düşünüyorum .
Julian

2
@ Blowsie- Plunks'i güncelledim. İşte orijinal (1.2RC3 olarak güncellendi): plnkr.co/edit/3Nwxxk?p=preview İşte hizmeti kullananlardan biri: plnkr.co/edit/a993Mn?p=preview
Pete BD

82

Basit olsun. Kadar basit

  1. promiseHizmetinize geri dönün (hizmette kullanmaya gerek yoktur then)
  2. Kullanım thencontroller içerisinde

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

Bağlantınızda, bu app.factoryve kodunuzda app.service. app.factoryBu durumda olması gerekiyordu .
Re Captcha

1
app.service de çalışır. Ayrıca - bu bana en zarif çözüm gibi görünüyor. Bir şey mi kaçırıyorum?
user1679130

1
Görünüşe göre her açısal problemim olduğunda @allenhwkim'in cevabı var! (Bu hafta 3. kez - harika ng-map bileşeni btw)
Yarin

Ben sadece burada status_code ile başarı ve hata koymak istiyorum
Anuj

58

Eşzamansız olduğundan $scope, ajax çağrısı tamamlanmadan önce verileri alıyor.

Sen kullanabilirsiniz $qoluşturmak için hizmet promiseve geri kontrolöre verin ve denetleyici içinde sonuç elde then()karşı çağrı promise.

Hizmetinizde,

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

Ardından, kontrol cihazınızda:

app.controller('MainCtrl', function( myService,$scope) {
  myService.async().then(function() {
    $scope.data = myService.data();
  });
});

2
+1 diğerlerinden daha fazla OO olduğu için bu en iyi gibi. Ancak bunu yapma herhangi bir neden yoktur this.async = function() {ve this.getData = function() {return data}? Umarım ne demek istediğimi anlarsın
bisiklet

@bicycle Aynı şekilde istedim ama işe yaramaz çünkü sözün sonuna kadar çözülmesi gerekiyor. Normalde yaptığınız gibi erişmez ve erişmeye çalışırsanız, dahili verilere erişirken bir referans hatası alırsınız. Umarım mantıklıdır?
user6123723

Doğru anlıyorsam, deffered = $q.defer()myService.async () 'i iki veya daha fazla kez aramak isterseniz myService.async içine eklemek gerekir
demas

1
Bu örnek, klasik bir ertelenmiş anti-modeldir . İle bir söz üretmek için gerek yoktur $q.deferolarak $httphizmet zaten bir söz verir. Döndürülen söz, $httpbir hata döndürürse askıya alınır . Ayrıca .successve .erroryöntemleri kullanımdan kaldırılmıştır ve AngularJS 1.6'dan kaldırılmıştır .
georgeawg

23

tosh shimayama bir çözüm var ama $ http vaat döner ve vaat bir değer döndürebilir gerçeğini kullanırsanız çok basitleştirebilirsiniz:

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

});

Kahve makinesinde küçük bir gösteri: http://plunker.no.de/edit/ksnErx?live=preview

Pistonunuz benim yöntemimle güncellendi: http://plnkr.co/edit/mwSZGK?p=preview


Yaklaşımınız boyunca daha fazla deneyeceğim. Ancak, sonucu döndürmek yerine hizmette yakalamak istiyorum. Bununla ilgili soruya buradan bakın stackoverflow.com/questions/12504747/… . $ Http tarafından döndürülen verileri denetleyicide farklı şekillerde işlemeyi seviyorum. yardımın için tekrar teşekkürler.
bsr

hizmetlerde vaatler kullanabilirsiniz, eğer $ watch'u beğenmezseniz ´promise.then (function (data) {service.data = data;}, onErrorCallback); ``
Guillaume86

Senden çatallı bir dalgıç ekledim
Guillaume86

1
alternatif olarak $ scope. $ hizmetten $ emit ve ctrl $ $ emit kontrolör size veri döndüğünü söylemek ama gerçekten bir yarar görmüyorum
Guillaume86

7

Bence çok daha iyi bir yol şöyle olurdu:

Hizmet:

app.service('FruitsManager',function($q){

    function getAllFruits(){
        var deferred = $q.defer();

        ...

        // somewhere here use: deferred.resolve(awesomeFruits);

        ...

        return deferred.promise;
    }

    return{
        getAllFruits:getAllFruits
    }

});

Ve denetleyicide şunları kullanabilirsiniz:

$scope.fruits = FruitsManager.getAllFruits();

Eğik otomatik giderilmiş koyacağız awesomeFruitsiçine $scope.fruits.


4
deferred.resolve ()? Daha kesin olun ve $ http çağrısı nerede? Ayrıca neden bir .service nesnesi döndürüyorsun?

6

Ben aynı sorunu vardı, ama ben internette sörf yaparken $ http varsayılan olarak geri bir söz geri döndü, o zaman "sonra" "veri" döndükten sonra kullanabilirsiniz. koda bakın:

 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

Kullanıcı arayüzünü dizinize bağlarken, uzunluğu 0 olarak ayarlayıp verileri diziye iterek aynı diziyi doğrudan güncellediğinizden emin olmak istersiniz.

Bunun yerine ( datakullanıcı arayüzünüzün bilmeyeceği farklı bir dizi referansı ayarlar ):

 myService.async = function() {
    $http.get('test.json')
    .success(function (d) {
      data = d;
    });
  };

bunu dene:

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

İşte yeni bir dizi ayarlama ile boşaltma ve mevcut bir diziye ekleme arasındaki farkı gösteren bir keman . Senin plnkr çalışamadım ama umarım bu sizin için çalışır!


bu işe yaramadı. konsol günlüğünde, d başarılı geri aramada düzgün bir şekilde güncellendiğini görebiliyordum, ancak veriyi görebiliyordum. Fonksiyon zaten yürütülmüş olabilir.
bsr

Bu yöntem kesinlikle çalışmalıdır belki bir dizi değil d veri türü ile ilgili bir şey vardır (örneğin asp.net dd için dd erişmek gerekir). Hatalı diziye bir dize gönderen bir örnek için bu plnkr'ye bakın: plnkr.co/edit/7FuwlN?p=preview
Gloopy

1
angular.copy(d, data)de çalışacak. Copy () yöntemine bir hedef sağlandığında, önce hedefin öğelerini siler ve ardından yenilerini kaynaktan kopyalar.
Mark Rajcok

4

Bununla ilgili olarak benzer bir sorun yaşadım, ancak Angular tarafından yapılan get veya post ile değil, 3. taraf tarafından yapılan bir uzantıyla (benim durumumda Chrome Uzantısı).
Karşılaştığım sorun, Chrome Uzantısının geri dönmemesi, then()bu yüzden yukarıdaki çözümde bunu yapamadım, ancak sonuç hala Asenkron.
Benim çözümüm bir hizmet yaratmak ve geri aramaya devam etmektir

app.service('cookieInfoService', function() {
    this.getInfo = function(callback) {
        var model = {};
        chrome.cookies.get({url:serverUrl, name:'userId'}, function (response) {
            model.response= response;
            callback(model);
        });
    };
});

Sonra kontrol cihazımda

app.controller("MyCtrl", function ($scope, cookieInfoService) {
    cookieInfoService.getInfo(function (info) {
        console.log(info);
    });
});

Bu başkalarının aynı sorunu elde etmesine yardımcı olabilir umuyoruz.


4

Http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/ okudum [AngularJS, çözülmüş olanı manuel olarak teslim etmek yerine doğrudan kapsam üzerine bir söz vererek denetleyici mantığımızı düzene koymamıza izin veriyor başarılı bir geri aramadaki değeri.]

çok basit ve kullanışlı :)

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

Umarım bu yardım


çalışmıyor. dönüş değeri defrred.promisebir işlev değildir.
Jürgen Paul

@PineappleUndertheSea neden bir işlev olması gerekiyor? Bu bir vaat nesnesidir.
Chev

@PineappleUndertheSea ertelenmiş ve defrred edilmemiş mi demek istediniz?
Derrick

2
PeteBD belirttiği gibi, bu formu $scope.items = Data.getData(); Anglular önerilmiyor
Poshest

2

Gerçekten, "söz" şeyler yapma yolu nedeniyle, $ http kullanan hizmetin tüketici yanıtı açmak için nasıl "bilmek" olması gerçeğini sevmiyorum.

Sadece bir şey aramak ve eski benzer verileri, almak istiyorum $scope.items = Data.getData();şeklinden, şimdi artık yok .

Bir süre denedim ve mükemmel bir çözüm bulamadım, ama işte en iyi atışım ( Plunker ). Birisi için yararlı olabilir.

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

Sonra denetleyici:

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

Zaten tespit edebileceğim kusurlar

  • Açısal'da sezgisel veya ortak bir desen olmayan verilerin eklenmesini istediğiniz nesneyi iletmeniz gerekir
  • getDataobjparametreyi yalnızca bir nesne biçiminde kabul edebilir (bir dizi de kabul edebilse de), bu birçok uygulama için sorun oluşturmaz, ancak bu bir boğaz sınırlamasıdır
  • Sen girdi nesnesi hazırlamak zorunda $scope.dataolan = {}bir nesne yapmak için (aslında neyi $scope.clearData()yukarıdaki yapar) veya = []bir dizi için, ya da değil çalışma (biz zaten veri geliyor ne hakkında bir şeyler varsaymak yaşıyorsanız) olacaktır. Bu hazırlık adım IN yapmaya çalıştım getData, ama şans yok.

Bununla birlikte, kontrolör "vaat paketini aç" kazan plakasını kaldıran bir desen sağlar ve DRY'yi tutarken $ http'den elde edilen belirli verileri birden fazla yerde kullanmak istediğiniz durumlarda yararlı olabilir.


1

Hizmetteki yanıtı önbelleğe alma konusunda, şimdiye kadar gördüğümden daha düz görünen başka bir sürüm:

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

    })();

});

bu hizmet önbelleğe alınmış verileri veya $http.get;

 dataStorage.then(function(data) {
     $scope.data = data;
 },function(e){
    console.log('err: ' + e);
 });

0

Lütfen aşağıdaki Kodu deneyin

Denetleyiciyi (PageCtrl) ve hizmeti (dataService) bölebilirsiniz

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.