Angularjs. Как редактируемые данные могут быть разделены между блоками запуска, фабрики и контроллера?

tl;dr — app.run сбрасывает значение $rootScope.variable каждый раз, когда я вызываю API — мне нужна помощь, чтобы найти способ обойти это.

У меня есть настройка веб-API AngularJS + ASP.NET. Мне нужно, чтобы AngularJS отправлял токен при каждом вызове API, кроме API входа в систему, я поместил этот код в свой app.run:

.run(function ($rootScope) {    
    // $rootScope.token = "" // <-- removed this initialization for now
    var sendToken = $rootScope.token == null ? "" : $rootScope.token;
    $.ajaxSetup({
        headers: {
            'myToken': sendToken;
        }
    });
}

Мой API входа в систему получает токен в своем ответе — я сохраняю это значение в $rootScope.token, а затем хочу отправить этот токен как значение «myToken» в заголовке HTTP всех других вызовов API. Таким образом, loginController должно быть разрешено обновлять $rootScope, а $.ajaxSetup должен получать обновленное значение $rootScope.token. Вот как мой loginController получает значение токена и обновляет $rootScope.token:

.controller('loginController', function($apiFactory, $rootScope) {
    $apiFactory.callAPI(
        '/api/login/login', 
        {'username': 'x', 'password': 'y'}, 
        function(apiResponse) {
            $rootScope.token = apiResponse.data;
    });
})

$apiFactory.callAPI — это стандартная функция, которую я создал на фабрике для фактического вызова API.

.factory('$apiFactory', function () {
    var root = {};  
    root.callAPI = function (apiName, data, successCB) {
        $.ajax({
            url: apiName,
            type: 'POST',
            data: JSON.stringify(data),
            contentType: 'application/json; charset=utf-8',
        }).done(function (apiResponse) {
            if (apiResponse.error == false) {
                successCB(apiResponse);
            }
        });
    }        
    return root;
}

LoginController успешно обновляет $rootScope.token, но когда я делаю следующий вызов API, он переходит к .run для запуска ajaxSetup и находит $rootScope.token неопределенным.

Что мне делать, чтобы исправить это? Большое спасибо!


comment
Вместо этого вы можете сохранить его в $apiFactory и использовать фабрику в .run. Но .run запускается только после запуска вашего углового приложения. Поэтому вы можете сохранить токен в LocalStorage.   -  person taguenizy    schedule 15.09.2016
comment
вы должны установить для него значение undefined, если вы находитесь на странице входа в систему. Таким образом, даже если вы его передадите, это не повлияет   -  person harishr    schedule 15.09.2016
comment
Почему в угловом приложении вы используете jquery AJAX?   -  person Stepan Kasyanenko    schedule 15.09.2016
comment
@StepanKasyanenko что посоветуете использовать вместо этого?   -  person Ekta    schedule 15.09.2016
comment
@entre Теперь я передаю его как неопределенное при входе в систему, но проблема в том, что он по-прежнему переходит к .run() для каждого вызова API и сбрасывает значение, когда оно туда попадает.   -  person Ekta    schedule 15.09.2016
comment
Вместо использования $.ajax нужно использовать $http сервис.   -  person Stepan Kasyanenko    schedule 15.09.2016
comment
@taguenizy - я попытался сохранить значение в $apiFactory и использовать фабрику в .run(). Похоже, что единственный раз, когда он теперь переходит к .run(), это до того, как API входа в систему устанавливает $apiFactory.token. Таким образом, .run() никогда не получает обновленное значение для отправки в своих заголовках. При вызове других API они также не получают обновленное значение $apiFactory.token.   -  person Ekta    schedule 15.09.2016
comment
@StepanKasyanenko Я могу использовать службу $http, но не останется ли эта проблема с отправкой значения в заголовке?   -  person Ekta    schedule 15.09.2016
comment
@Ekta на 'myToken': sendToken; использует $apiFactory.token, чтобы сохранить ссылку на него. Поскольку run запускается только один раз, вам нужно сохранить ссылку на токен. И сохраните его на контроллере входа в систему с помощью $apiFactory.token = apiResponse.data; :)   -  person taguenizy    schedule 15.09.2016
comment
Смотрите мой ответ. Он отлично работает без run или config.   -  person Stepan Kasyanenko    schedule 15.09.2016
comment
@taguenizy - Спасибо, все еще не повезло :(. Я инициализировал token = "abc") в apifactory. Затем в .run() у меня есть 'myToken': $apiFactory.token. Затем в loginController я обновляю значение на $apiFactory.token = "def". Затем в моем layoutController, где я вызываю другой API, значение, которое он отправляет для myToken (при анализе заголовка), по-прежнему равно abc, а не def.   -  person Ekta    schedule 15.09.2016


Ответы (2)


$rootScope.$broadcast отправляет событие через область приложения. Любая дочерняя область этого приложения может поймать его с помощью простого: $scope.$on().

$rootScope.$broadcast("hi");

$rootScope.$on("hi", function(){
    //do something
});

.service("hiEventService",function($rootScope) {
    this.broadcast = function() {$rootScope.$broadcast("hi")}
    this.listen = function(callback) {$rootScope.$on("hi",callback)}
})
person Michal Kucaj    schedule 15.09.2016
comment
Спасибо! Это работает очень хорошо, когда я добавил $timeout (как предлагается здесь: stackoverflow.com/questions/29769804/). Однако, когда я обновляю страницу, она теряет все значения, как, вероятно, и должно быть. Могу ли я в любом случае сохранить значения при обновлении? Я подумал о том, чтобы сохранить его в файле cookie, но файлы cookie будут видны из консоли разработчика, поэтому я не могу этого сделать. - person Ekta; 16.09.2016
comment
Использование сеанса Windows также может быть не очень хорошей идеей, потому что я использую веб-API и хотел бы сохранить безгражданство. - person Ekta; 16.09.2016

Мы можем добавить\расширить заголовки по умолчанию в вызове AJAX.

Вам нужно использовать $http сервис.

Пример на jsfiddle.

angular.module('ExampleApp', [])
  .controller('ExampleController', function(ExampleService, AuthService) {
    var vm = this;
    vm.start = function() {
      ExampleService.getData();
    };
    vm.auth = function() {
      AuthService.auth();
    }
  })
  .service('ExampleService', function($http) {
    return {
      getData: function() {
        return $http.get("/urlsource");
      }
    }
  }).service('AuthService', function($http) {
    return {
      auth: function() {
        return $http.get("/auth")
          .then(function(apiResponse) {
            //after success auth add token
            $http.defaults.headers.common['myToken'] = apiResponse.data;
          })
          .catch(function() {
            // just for example in jsfiddle, because we got 404 error for request `auth` url
            $http.defaults.headers.common['myToken'] = '12314dsfsfsd';
          });
      }
    }
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.js"></script>
<div ng-app="ExampleApp">
  <div ng-controller="ExampleController as vm">
    <pre>Steps:
  1) Press "Start Request" button and check in console header
  2) Press "Simulate auth" button. This is add default header "myToken"
  3) Press "Start Request" button and check in console header "myToken".
  </pre>
    <button ng-click="vm.start()">
      Start Request
    </button>
    <button ng-click="vm.auth()">
      Simulate auth
    </button>
  </div>
</div>

К сожалению, сниппет не работает. Но jsfiddle работает нормально.

person Stepan Kasyanenko    schedule 15.09.2016