Jaka jest najlepsza praktyka wykonywania wywołań AJAX w Angular.js?


151

Czytałem ten artykuł: http://eviltrout.com/2013/06/15/ember-vs-angular.html

I powiedział:

Z powodu braku konwencji zastanawiam się, ile projektów Angular opiera się na złych praktykach, takich jak wywołania AJAX bezpośrednio w kontrolerach? Czy z powodu wstrzykiwania zależności programiści wprowadzają parametry routera do dyrektyw? Czy początkujący programiści AngularJS zamierzają ustrukturyzować swój kod w sposób, który zdaniem doświadczonego programisty AngularJS jest idiomatyczny?

W rzeczywistości $httpwykonuję połączenia z kontrolera Angular.js. Dlaczego jest to zła praktyka? Jaka jest zatem najlepsza praktyka wykonywania $httppołączeń? i dlaczego?


12
+1 za odwołanie się do interesującego posta porównującego ember i angularjs.
Chandermani

To samo zastanawiałem się nad najlepszymi praktykami Angular
Dalorzo

Uzupełnieniem jest również sprawdzenie API pod kątem rzeczy, które mogłeś przegapić: docs.angularjs.org/api/ng/service/$http
Christophe Roussy.

Odpowiedzi:


174

EDYCJA: Ta odpowiedź dotyczyła głównie wersji 1.0.X. Aby uniknąć nieporozumień, zmieniono go, aby odzwierciedlał najlepszą odpowiedź dla WSZYSTKICH aktualnych wersji Angular na dzień dzisiejszy, 05.12.2013.

Chodzi o to, aby utworzyć usługę, która zwraca obietnicę zwracanych danych, a następnie wywołać ją w kontrolerze i obsłużyć tam obietnicę, aby zapełnić twoją właściwość $ scope.

Obsługa

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

Kontroler:

Zajmij się then()metodą obietnicy i wyciągnij z niej dane. Ustaw właściwość $ scope i zrób wszystko, co może być konieczne.

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

Rozwiązanie In-View Promise (tylko 1.0.X):

W Angular 1.0.X, który jest celem pierwotnej odpowiedzi tutaj, obietnice zostaną potraktowane w specjalny sposób przez Widok. Po rozstrzygnięciu ich rozstrzygnięta wartość zostanie powiązana z widokiem. W wersji 1.2.X jest to przestarzałe

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
Wystarczy wspomnieć, że działa to tylko wtedy, gdy używasz $scope.fooswłaściwości w szablonie. Gdybyś miał użyć tej samej właściwości poza szablonem (na przykład w innej funkcji), przechowywany tam obiekt nadal jest obiektem obietnicy.
Clark Pan,

1
Obecnie używam tego wzorca w nowej aplikacji kątowej, jednak zastanawiam się na prostej stronie, jak uzyskać dostęp do właściwości, którą powiązałem z zakresem, w tym przykładzie, jeśli chcę pobrać dane z getFoos i opublikować zmiany w to. jeśli spróbuję uzyskać dostęp do $ scope.foos w mojej aktualizacji, mam obiekt obietnicy, a nie dane, mogę zobaczyć, jak uzyskać dane w samym obiekcie, ale wydaje się, że jest to naprawdę hacky.ideas?
Kelly Milligan

5
@KellyMilligan, w tym wzorze, to wiązanie wie, co zrobić z obietnicą. Jeśli chcesz uzyskać dostęp do obiektu z dowolnego miejsca, będziesz musiał poradzić sobie .then()z obietnicą i umieścić wartość w $ scope ...myService.getFoos().then(function(value) { $scope.foos = value; });
Ben Lesh

1
Tylko aktualizacja tej techniki, od wersji 1.2.0-rc.3, automatyczne rozpakowywanie obietnic zostało wycofane, więc ta technika nie będzie już działać.
Clark Pan

2
Niedawno dostałem kilka głosów przeciw, prawdopodobnie dlatego, że nie było już zgodne z najnowszą wersją Angular. Zaktualizowałem odpowiedź, aby to odzwierciedlić.
Ben Lesh,

45

Najlepszą praktyką byłoby przeniesienie $httpwywołania do „usługi”, która dostarcza dane Twojemu administratorowi:

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

Abstrahowanie $httpwywołania w ten sposób umożliwi ponowne użycie tego kodu na wielu kontrolerach. Staje się to konieczne, gdy kod, który wchodzi w interakcję z tymi danymi, staje się bardziej złożony, być może chcesz przetworzyć dane przed użyciem ich w kontrolerze i zapisać wynik tego procesu w pamięci podręcznej, abyś nie musiał tracić czasu na ich ponowne przetwarzanie.

Powinieneś myśleć o „usłudze” jako o reprezentacji (lub modelu) danych, z których może korzystać Twoja aplikacja.


9

Zaakceptowana odpowiedź dawała mi $http is not definedbłąd, więc musiałem to zrobić:

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

Główną różnicą jest ta linia:

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

1

Umieściłem odpowiedź dla kogoś, kto chciał mieć całkowicie ogólną usługę internetową w Angular. Zalecam po prostu podłączenie go, a zajmie się wszystkimi wywołaniami usług internetowych bez konieczności kodowania ich wszystkich samodzielnie. Odpowiedź jest tutaj:

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.