0

tl;dr - app.run resets $rootScope.variable's value every time I invoke an API - I need help finding a way around that.

I have an AngularJS + ASP.NET Web API setup. I need AngularJS to send a token in every API call except login API, I have placed this code in my app.run:

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

My login API gets the token in its response - I save that value in $rootScope.token and then I want to send that token as a value of 'myToken' in the HTTP header of all other API calls. So loginController should be allowed to update $rootScope and $.ajaxSetup should get updated value of $rootScope.token. This is how my loginController gets the token value and updates $rootScope.token:

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

$apiFactory.callAPI is a standard function I've made in a factory for the actual API call.

.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 successfully updates $rootScope.token, but when I make the next API call, it goes to .run to run the ajaxSetup, and finds $rootScope.token as undefined.

What should I do to fix this? Thanks a lot!

Ekta
  • 27
  • 10
  • You could store it in the `$apiFactory` instead and use the factory in `.run`. But `.run` only runs once your angular app starts. So you might want to persist the token in [LocalStorage](https://developer.mozilla.org/en/docs/Web/API/Window/localStorage) – taguenizy Sep 15 '16 at 09:05
  • you should set it to undefined if you are on login page.. that ways even if you pass it, it wont have impact – harishr Sep 15 '16 at 09:06
  • Why in angular app you are using `jquery` AJAX? – Stepan Kasyanenko Sep 15 '16 at 09:08
  • @StepanKasyanenko what do you recommend using instead? – Ekta Sep 15 '16 at 09:41
  • @entre I am now passing it as undefined in login, but the problem is that it still goes to .run() for every API call and it resets the value when it gets there. – Ekta Sep 15 '16 at 09:44
  • Instead of using `$.ajax` need use `$http` service. – Stepan Kasyanenko Sep 15 '16 at 09:46
  • @taguenizy - I tried storing the value in $apiFactory and using the factory in .run(). It looks like the only time it now goes to .run() is before the login api sets $apiFactory.token. So .run() never gets the updated value to send in its headers. When other APIs are invoked, they also don't get the updated value of $apiFactory.token. – Ekta Sep 15 '16 at 10:05
  • @StepanKasyanenko I can use $http service, but wouldn't this problem of sending the value in header still remain? – Ekta Sep 15 '16 at 10:06
  • @Ekta on `'myToken': sendToken;` use `$apiFactory.token` so it keeps the reference to it. Because `run` only runs once so you need to keep the reference to where the token is. And store it on the login controller by `$apiFactory.token = apiResponse.data;` :) – taguenizy Sep 15 '16 at 10:07
  • See my answer. It work fine without `run` or `config`. – Stepan Kasyanenko Sep 15 '16 at 10:10
  • @taguenizy - Thanks, still no luck :(. I initialized `token = "abc"`) in apifactory. Then in .run(), I have `'myToken': $apiFactory.token`. Then in loginController, I'm updating the value by `$apiFactory.token = "def"`. Then in my layoutController where I call another API, the value it sends for `myToken` (when the header is parsed) is still "abc" not "def". – Ekta Sep 15 '16 at 10:20

2 Answers2

0

$rootScope.$broadcast is sending an event through the application scope. Any children scope of that app can catch it using a simple: $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)}
})
Michal Kucaj
  • 681
  • 5
  • 15
  • Thanks! This works very well once I added $timeout (as suggested here: http://stackoverflow.com/questions/29769804/getting-and-setting-value-in-factory-in-angualrjs). However, when I refresh the page, it loses all the values, as it probably should. Is there anyway I could still retain the values upon refresh? I considered storing it in a cookie, but cookies would be visible from developer console, so can't do that. – Ekta Sep 16 '16 at 01:42
  • Using a windows session might not be a good idea either, because I'm using Web API and would like to maintain the statelessness. – Ekta Sep 16 '16 at 02:40
0

We can add\extend default headers in AJAX call.

You need use $http service.

Example on 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>

Unfortunately, snippet doesn't work. But jsfiddle work fine.

Stepan Kasyanenko
  • 3,176
  • 1
  • 15
  • 23