Как заставить контроллер ждать обещания решить из службы angular

У меня есть служба, которая делает запрос AJAX для бэкэнд

Сервис:

    function GetCompaniesService(options)
    {
        this.url = '/company';
        this.Companies = undefined;
        this.CompaniesPromise = $http.get(this.url);

    }

Контроллер:

var CompaniesOb = new GetCompanies();
CompaniesOb.CompaniesPromise.then(function(data){
   $scope.Companies = data;
});

Я хочу, чтобы моя служба обрабатывала функцию ".then" вместо того, чтобы обрабатывать ее в моем контроллере, и я хочу, чтобы мой контроллер работал с этими данными из службы после того, как обещание внутри службы были разрешены.

В принципе, я хочу иметь доступ к таким данным:

var CompaniesOb = new GetCompanies();
$scope.Companies = CompaniesOb.Companies;

С разрешением обещания, которое обрабатывается внутри самой службы.

Возможно ли это? Или это единственный способ получить доступ к этому обещанию с разрешения вне службы?

Ответ 1

Если вы хотите обработать ответ $http в самой службе, вы можете добавить функцию then в службу, где вы выполняете больше обработки, а затем return от этой функции then, например

function GetCompaniesService(options) {
  this.url = '/company';
  this.Companies = undefined;
  this.CompaniesPromise = $http.get(this.url).then(function(response) {
    /* handle response then */
    return response
  })
}

Но вы все равно будете использовать обещание в контроллере, но то, что вы получите, уже будет обработано в службе.

var CompaniesOb = new GetCompanies();
CompaniesOb.CompaniesPromise.then(function(dataAlreadyHandledInService) {
  $scope.Companies = dataAlreadyHandledInService;
});

Ответ 2

Нет проблем для достижения этого!

Главное, что вы должны иметь в виду, это то, что вам нужно сохранить ту же ссылку на объект (а в массивах javascript - объекты) в вашей службе.

вот наш простой HTML:

<div ng-controller = "companiesCtrl"> 
  <ul ng-repeat="company in companies">
     <li>{{company}}</li>
  </ul>
</div>

Вот наша реализация сервиса:

serviceDataCaching.service('companiesSrv', ['$timeout', function($timeout){
  var self = this;

  var httpResult = [
    'company 1',
    'company 2',
    'company 3'
  ];

  this.companies = ['preloaded company'];
  this.getCompanies = function() {
    // we simulate an async operation
    return $timeout(function(){
      // keep the array object reference!!
      self.companies.splice(0, self.companies.length);

      // if you use the following code:
      // self.companies = [];
      // the controller will loose the reference to the array object as we are creating an new one
      // as a result it will no longer get the changes made here!
      for(var i=0; i< httpResult.length; i++){
        self.companies.push(httpResult[i]);
      }
      return self.companies;
    }, 3000);                    
}}]);   

И, наконец, контроллер, как вы этого хотели:

serviceDataCaching.controller('companiesCtrl', function ($scope, companiesSrv) {
  $scope.companies = companiesSrv.companies;
  companiesSrv.getCompanies();
});

Разъяснения

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

Вот скрипка, которая его обертывает.

В комментариях кода вы можете попытаться разогнать часть, которая не работает, и вы увидите, как контроллер теряет ссылку. Фактически контроллер будет продолжать ссылаться на старый массив, пока служба изменит новый.

Последнее важное: помните, что $timeout запускает $apply() на rootSCope. Именно поэтому область нашего контроллера освежает "одиночку". Без него, и если вы попытаетесь заменить его обычным setTimeout(), вы увидите, что контроллер не обновляет список компаний. Чтобы обойти это, вы можете:

  • ничего не делать, если ваши данные извлекаются с помощью $http, поскольку он вызывает $apply при успешном завершении
  • wrap вы получите $timeout (..., 0);
  • введите $rootSCope в службу и вызовите $apply() на нем, когда выполняется асинхронная операция.
  • в контроллере добавьте $scope. $apply() в успешном завершении getCompanies().

Надеюсь, это поможет!

Ответ 3

Вы можете передать $scope в GetCompanies и установить $scope.Companies к данным в сервисе

 function GetCompaniesService(options,scope)
 {
    this.url = '/company';
    this.Companies = undefined;
    this.CompaniesPromise = $http.get(this.url).then(function(res) {
      scope.Companies = res;
    });

 }

Вы должны быть осторожны с порядком, в котором вы используете данные. Такова причина обещания начать.