Qual é a melhor prática para fazer uma chamada AJAX no Angular.js?

151

Eu estava lendo este artigo: http://eviltrout.com/2013/06/15/ember-vs-angular.html

E dizia:

Devido à falta de convenções, pergunto-me quantos projetos Angular dependem de práticas ruins, como chamadas AJAX diretamente nos controladores? Devido à injeção de dependência, os desenvolvedores estão injetando parâmetros do roteador nas diretivas? Os desenvolvedores iniciantes do AngularJS vão estruturar seu código de maneira que um desenvolvedor experiente do AngularJS acredite ser idiomático?

Na verdade, estou fazendo $httpchamadas do meu controlador Angular.js. Por que é uma má prática? Qual é a melhor prática para fazer $httpchamadas então? e porque?

morango
fonte
12
+1 por se referir a post interessante comparando brasa e angularjs.
21413 Chandermani
Perguntei-
Também um complemento também verificar a API para coisas que você pode ter faltado: docs.angularjs.org/api/ng/service/$http
Christophe Roussy

Respostas:

174

EDIT: Esta resposta foi focada principalmente na versão 1.0.X. Para evitar confusão, está sendo alterado para refletir a melhor resposta para TODAS as versões atuais do Angular até hoje, 05/12/2013.

A idéia é criar um serviço que retorne uma promessa aos dados retornados, depois chame isso em seu controlador e lide com a promessa para preencher sua propriedade $ scope.

O serviço

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

O controlador:

Manipule o then()método da promessa e obtenha os dados. Defina a propriedade $ scope e faça o que mais for necessário.

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

Resolução Promise In-View (somente 1.0.X):

No Angular 1.0.X, o objetivo da resposta original aqui, as promessas receberão um tratamento especial da View. Quando eles resolverem, seu valor resolvido será vinculado à exibição. Este foi descontinuado no 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
fonte
4
Apenas para mencionar, isso só funciona quando você usa uma $scope.foospropriedade em um modelo. Se você usar essa mesma propriedade fora de um modelo (por exemplo, em outra função), o objeto armazenado ainda será um objeto de promessa.
Clark Pan
1
No momento, estou usando esse padrão em um novo aplicativo angular, no entanto, em uma página crud, estou imaginando como obter acesso à propriedade que vinculei ao escopo; neste exemplo, se você quiser pegar os dados do getFoos e postar alterações em isto. se eu tentar acessar o $ scope.foos na minha atualização, tenho o objeto de promessa e não os dados, posso ver como obter os dados no próprio objeto, mas parece realmente realmente hacky.ideas?
precisa
5
@KellyMilligan, nesse padrão, é a ligação que sabe o que fazer com a promessa. Se você precisar acessar o objeto de qualquer outro lugar, você vai ter que lidar com a .then()da promessa e colocar o valor em US $ âmbito ...myService.getFoos().then(function(value) { $scope.foos = value; });
Ben Lesh
1
Apenas uma atualização dessa técnica, a partir da versão 1.2.0-rc.3, o desempacotamento automático de promessas foi preterido, portanto, essa técnica não funcionará mais.
Clark Pan
2
Recebi algumas votações negativas aqui recentemente, presumivelmente porque não estava mais alinhada com a versão mais recente do Angular. Atualizei a resposta para refletir isso.
Ben Lesh
45

A melhor prática seria abstrair a $httpchamada em um 'serviço' que fornece dados ao seu 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.
    })
});

Abstrair a $httpchamada assim permitirá reutilizar esse código em vários controladores. Isso se torna necessário quando o código que interage com esses dados se torna mais complexo, talvez você deseje processar os dados antes de usá-los em seu controlador e armazenar em cache o resultado desse processo, para que você não precise gastar tempo re-processando-os.

Você deve pensar no 'serviço' como uma representação (ou modelo) de dados que seu aplicativo pode usar.

Clark Pan
fonte
9

A resposta aceita estava me dando o $http is not definederro, então tive que fazer o seguinte:

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

A principal diferença é esta linha:

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

Eu coloquei uma resposta para alguém que queria um serviço da web totalmente genérico em Angular. Eu recomendo apenas conectá-lo e ele cuidará de todas as suas chamadas de serviço da web sem a necessidade de codificá-las. A resposta está aqui:

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

cullimorer
fonte