Как лучше всего выполнять вызов AJAX в Angular.js?

151

Я читал эту статью: http://eviltrout.com/2013/06/15/ember-vs-angular.html

И он сказал,

Из-за отсутствия соглашений мне интересно, сколько проектов Angular полагаются на плохую практику, такую ​​как вызовы AJAX непосредственно в контроллерах? Из-за внедрения зависимостей разработчики вводят параметры маршрутизатора в директивы? Будут ли начинающие разработчики AngularJS структурировать свой код таким образом, который опытный разработчик AngularJS считает идиоматическим?

Я на самом деле $httpзвоню с моего контроллера Angular.js. Почему это плохая практика? Какова лучшая практика для $httpзвонков тогда? и почему?

клубника
источник
12
+1 за ссылку на интересный пост, сравнивающий ember и angularjs.
Чандермани
Я задавался вопросом о лучших угловых практиках
Dalorzo
Также в дополнение также проверьте API на предмет того, что вы, возможно, пропустили: docs.angularjs.org/api/ng/service/$http
Кристоф Руссси,

Ответы:

174

РЕДАКТИРОВАТЬ: Этот ответ был в основном сосредоточен на версии 1.0.X. Во избежание путаницы он был изменен, чтобы отразить лучший ответ для ВСЕХ текущих версий Angular на сегодня, 2013-12-05.

Идея состоит в том, чтобы создать сервис, который возвращает обещание возвращенным данным, затем вызвать его в вашем контроллере и обработать обещание, чтобы заполнить свойство $ scope.

Сервис

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

Контроллер:

Обработайте метод обещания then()и извлеките из него данные. Установите свойство $ scope и делайте все, что вам может понадобиться.

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

Разрешение In-View Promise (только 1.0.X):

В Angular 1.0.X, цель исходного ответа здесь, обещания получат особую обработку при просмотре. Когда они разрешаются, их разрешенное значение будет привязано к представлению. Это устарело в 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
Просто отметим, что это работает только тогда, когда вы используете $scope.foosсвойство в шаблоне. Если бы вы использовали это же свойство за пределами шаблона (например, в другой функции), сохраненный там объект все еще остается объектом обещания.
Кларк Пэн
1
В настоящее время я использую этот шаблон в новом угловом приложении, однако мне не совсем понятно, как получить доступ к свойству, которое я привязал к области действия, в этом примере, если я хочу получить данные из getFoos и опубликовать изменения в Это. если я попытаюсь получить доступ к $ scope.foos в моем обновлении, у меня будет объект обещания, а не данные, я смогу увидеть, как получить данные в самом объекте, но это действительно очень похоже на hacky.ideas?
Келли Миллиган
5
@KellyMilligan, в этом шаблоне привязка знает, что делать с обещанием. Если вам нужен доступ к объекту из любого другого места, вам придется обработать .then()обещание и поместить значение в $ scope ...myService.getFoos().then(function(value) { $scope.foos = value; });
Бен Леш,
1
Просто обновление этой методики, начиная с 1.2.0-rc.3, автоматическая распаковка обещаний устарела, поэтому эта методика больше не будет работать.
Кларк Пан
2
Недавно я получил пару отрицательных отзывов, по-видимому, потому что это больше не соответствовало последней версии Angular. Я обновил ответ, чтобы отразить это.
Бен Леш
45

Рекомендуется абстрагировать $httpвызов в «сервис», который предоставляет данные вашему контроллеру:

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

Абстрагирование $httpвызова таким образом позволит вам повторно использовать этот код на нескольких контроллерах. Это становится необходимым, когда код, который взаимодействует с этими данными, становится более сложным, возможно, вы захотите обработать данные перед их использованием в контроллере и кэшировать результаты этого процесса, чтобы вам не пришлось тратить время на их повторную обработку.

Вы должны думать о «сервисе» как о представлении (или модели) данных, которые может использовать ваше приложение.

Кларк Пан
источник
9

Принятый ответ дал мне $http is not definedошибку, поэтому я должен был сделать это:

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

Основное отличие заключается в этой строке:

policyService.service('PolicyService', ['$http', function ($http) {
user1477388
источник
1

Я поставил ответ для кого-то, кто хотел полностью общий веб-сервис на Angular. Я бы порекомендовал просто подключить его, и он позаботится обо всех ваших вызовах веб-службы без необходимости кодировать их все самостоятельно. Ответ здесь:

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

cullimorer
источник