Qual è la migliore pratica per effettuare una chiamata AJAX in Angular.js?


151

Stavo leggendo questo articolo: http://eviltrout.com/2013/06/15/ember-vs-angular.html

E diceva:

A causa della mancanza di convenzioni, mi chiedo quanti progetti angolari si basano su cattive pratiche come le chiamate AJAX direttamente all'interno dei controller? A causa dell'iniezione di dipendenza, gli sviluppatori stanno iniettando i parametri del router nelle direttive? Gli sviluppatori AngularJS inesperti struttureranno il loro codice in un modo che uno sviluppatore AngularJS esperto ritiene idiomatico?

$httpAttualmente sto effettuando chiamate dal mio controller Angular.js. Perché è una cattiva pratica? Qual è la migliore pratica per effettuare $httpchiamate allora? e perché?


12
+1 per riferirsi a post interessanti confrontando ember e angularjs.
Chandermani,

Mi chiedevo lo stesso delle migliori pratiche angolari
Dalorzo,

Inoltre un complemento controlla anche l'API per le cose che potresti aver perso: docs.angularjs.org/api/ng/service/$http
Christophe Roussy,

Risposte:


174

EDIT: questa risposta era principalmente focalizzata sulla versione 1.0.X. Per evitare confusione, è stato modificato per riflettere la migliore risposta per TUTTE le versioni attuali di Angular a partire da oggi, 2013-12-05.

L'idea è quella di creare un servizio che restituisca una promessa ai dati restituiti, quindi chiamarlo nel controller e gestire la promessa lì per popolare la proprietà $ scope.

Il servizio

module.factory('myService', function($http) {
   return {
        getFoos: function() {
             //return the promise directly.
             return $http.get('/foos')
                       .then(function(result) {
                            //resolve the promise as the data
                            return result.data;
                        });
        }
   }
});

Il controller:

Gestisci il then()metodo della promessa e ne ricava i dati. Imposta la proprietà $ scope e fai tutto il necessario.

module.controller('MyCtrl', function($scope, myService) {
    myService.getFoos().then(function(foos) {
        $scope.foos = foos;
    });
});

Risoluzione promessa in-view (solo 1.0.X):

In Angular 1.0.X, l'obiettivo della risposta originale qui, le promesse riceveranno un trattamento speciale da parte della Vista. Quando si risolvono, il loro valore risolto sarà associato alla vista. Questo è stato deprecato in 1.2.X

module.controller('MyCtrl', function($scope, myService) {
    // now you can just call it and stick it in a $scope property.
    // it will update the view when it resolves.
    $scope.foos = myService.getFoos();
});

4
Solo per citare, questo funziona solo quando si utilizza una $scope.foosproprietà in un modello. Se dovessi usare la stessa proprietà al di fuori di un modello (ad esempio in un'altra funzione), l'oggetto memorizzato lì è ancora un oggetto promessa.
Clark Pan,

1
Attualmente sto usando questo modello in una nuova app angolare, tuttavia mi chiedo in una pagina grezza come ottenere l'accesso alla proprietà che ho legato all'ambito, in questo esempio se volevo prendere i dati da getFoos e pubblicare le modifiche su esso. se provo ad accedere a $ scope.foos nel mio aggiornamento, ho l'oggetto promessa e non i dati, posso vedere come ottenere i dati nell'oggetto stesso, ma sembra davvero hacky.ideas?
Kelly Milligan,

5
@KellyMilligan, in questo schema, è l' associazione che sa cosa fare della promessa. Se devi accedere all'oggetto da qualsiasi altra parte, dovrai gestire .then()la promessa e inserire il valore in $ scope ...myService.getFoos().then(function(value) { $scope.foos = value; });
Ben Lesh

1
Solo un aggiornamento su questa tecnica, a partire da 1.2.0-rc.3, il disimballaggio automatico delle promesse è stato deprecato, quindi questa tecnica non funzionerà più.
Clark Pan,

2
Recentemente ho ottenuto un paio di voti negativi, presumibilmente perché non era più in linea con l'ultima versione di Angular. Ho aggiornato la risposta per riflettere ciò.
Ben Lesh,

45

La migliore pratica sarebbe quella di astrarre la $httpchiamata in un "servizio" che fornisca dati al tuo controller:

module.factory('WidgetData', function($http){
    return {
        get : function(params){
            return $http.get('url/to/widget/data', {
                params : params
            });
        }
    }
});

module.controller('WidgetController', function(WidgetData){
    WidgetData.get({
        id : '0'
    }).then(function(response){
        //Do what you will with the data.
    })
});

Astrarre la $httpchiamata in questo modo ti consentirà di riutilizzare questo codice su più controller. Ciò diventa necessario quando il codice che interagisce con questi dati diventa più complesso, forse si desidera elaborare i dati prima di utilizzarli nel controller e memorizzare nella cache il risultato di tale processo in modo da non dover perdere tempo a rielaborarli.

È necessario considerare il "servizio" come una rappresentazione (o modello) dei dati che l'applicazione può utilizzare.


9

La risposta accettata mi stava dando l' $http is not definederrore, quindi ho dovuto fare questo:

var policyService = angular.module("PolicyService", []);
policyService.service('PolicyService', ['$http', function ($http) {
    return {
        foo: "bar",
        bar: function (params) {
            return $http.get('../Home/Policy_Read', {
                params: params
            });
        }
    };
}]);

La differenza principale è questa linea:

policyService.service('PolicyService', ['$http', function ($http) {

1

Ho messo una risposta per qualcuno che voleva un servizio web totalmente generico in Angular. Ti consiglierei di collegarlo e si occuperà di tutte le tue chiamate al servizio web senza bisogno di codificarle tutte da solo. La risposta è qui:

https://stackoverflow.com/a/38958644/5349719

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.