¿Cuál es la mejor práctica para hacer una llamada AJAX en Angular.js?

151

Estaba leyendo este artículo: http://eviltrout.com/2013/06/15/ember-vs-angular.html

Y decía:

Debido a su falta de convenciones, me pregunto cuántos proyectos angulares se basan en malas prácticas, como llamadas AJAX directamente dentro de los controladores. Debido a la inyección de dependencia, ¿los desarrolladores están inyectando parámetros de enrutador en las directivas? ¿Los desarrolladores novatos de AngularJS van a estructurar su código de una manera que un desarrollador experimentado de AngularJS cree que es idiomático?

Realmente estoy haciendo $httpllamadas desde mi controlador Angular.js. ¿Por qué es una mala práctica? ¿Cuál es la mejor práctica para hacer $httpllamadas entonces? ¿y por qué?

fresa
fuente
12
+1 para referirse a publicaciones interesantes que comparan ascua y angularjs.
Chandermani
Me preguntaba lo mismo sobre las mejores prácticas
angulares
También un complemento también verifique en la API las cosas que puede haber perdido: docs.angularjs.org/api/ng/service/$http
Christophe Roussy

Respuestas:

174

EDITAR: Esta respuesta se centró principalmente en la versión 1.0.X. Para evitar confusiones, se está cambiando para reflejar la mejor respuesta para TODAS las versiones actuales de Angular a partir de hoy, 2013-12-05.

La idea es crear un servicio que devuelva una promesa a los datos devueltos, luego llame a eso en su controlador y maneje la promesa allí para completar su propiedad $ scope.

El servicio

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

El controlador:

Maneje el then()método de la promesa y obtenga los datos de él. Establezca la propiedad $ scope y haga lo que sea que necesite hacer.

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

Resolución de promesa a la vista (solo 1.0.X):

En Angular 1.0.X, el objetivo de la respuesta original aquí, las promesas recibirán un tratamiento especial por parte de la Vista. Cuando se resuelven, su valor resuelto estará vinculado a la vista. Esto ha quedado en desuso en 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();
});
Ben Lesh
fuente
44
Solo para mencionar, esto solo funciona cuando usa una $scope.foospropiedad en una plantilla. Si tuviera que usar esa misma propiedad fuera de una plantilla (por ejemplo, en otra función), el objeto almacenado allí sigue siendo un objeto de promesa.
Clark Pan
1
Actualmente estoy usando este patrón en una nueva aplicación angular, sin embargo, me pregunto en una página grosera cómo obtener acceso a la propiedad que vinculé al alcance, en este ejemplo si quisiera tomar los datos de getFoos y publicar cambios en eso. si trato de acceder a $ scope.foos en mi actualización, tengo el objeto de promesa y no los datos, puedo ver cómo obtener los datos en el objeto en sí, pero parece realmente hacky.ideas?
Kelly Milligan
55
@KellyMilligan, en este patrón, es el enlace el que sabe qué hacer con la promesa. Si necesita acceder al objeto desde cualquier otro lugar, tendrá que manejar .then()la promesa y poner el valor en $ alcance ...myService.getFoos().then(function(value) { $scope.foos = value; });
Ben Lesh
1
Solo una actualización de esta técnica, a partir de 1.2.0-rc.3, el desempaquetado automático de promesas ha quedado en desuso, por lo que esta técnica ya no funcionará.
Clark Pan
2
Recibí un par de votos negativos aquí recientemente, presumiblemente porque ya no estaba en línea con la última versión de Angular. He actualizado la respuesta para reflejar eso.
Ben Lesh
45

La mejor práctica sería abstraer la $httpllamada en un 'servicio' que proporciona datos a su controlador:

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

Si abstrae la $httpllamada de esta manera, podrá reutilizar este código en varios controladores. Esto se hace necesario cuando el código que interactúa con estos datos se vuelve más complejo, tal vez desee procesar los datos antes de usarlos en su controlador y almacenar en caché el resultado de ese proceso para que no tenga que perder tiempo volviéndolo a procesar.

Debe pensar en el 'servicio' como una representación (o Modelo) de datos que su aplicación puede usar.

Clark Pan
fuente
9

La respuesta aceptada me estaba dando el $http is not definederror, así que tuve que hacer esto:

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 principal diferencia es esta línea:

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

Puse una respuesta para alguien que quería un servicio web totalmente genérico en Angular. Recomiendo simplemente enchufarlo y se encargará de todas sus llamadas al servicio web sin necesidad de codificarlas usted mismo. La respuesta está aquí:

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

cullimorer
fuente