0

I have an AngularJS (v1.5) client that is used to schedule an Operating Room at a hospital. The app has a single controller and two functions within that controller. These two functions are both $http POST requests.

The first method called (requestAccessToken) passes a username, password, and grant_type to receive back an OAuth 2 access token which is then assigned to a variable on the scope. This works fine.

The second method (scheduleORSuite) makes an API call passing the access token obtained from the first method (requestAccessToken) . The problem is that when the method () executes the access token is null. I know I am receiving back a valid access token because when I call the method requestAccessToken directly I get back a valid access token. When I step through the debugger in Chrome it looks like the method (scheduleORSuite), that uses the access token, does not even wait for the method that obtains the access token to return.

<script type="text/javascript">
    var scheduleORSuiteApp = angular.module('scheduleORSuiteApp', []);

    scheduleORSuiteApp.controller('ScheduleORSuiteController', function ($scope, $http) {
        var viewModel = this;
        viewModel.accessToken = null;
        viewModel.userName = 'theUserName';
        viewModel.password = 'thePassword';

        // This method requests the access token
        viewModel.requestAccessToken = function () {
            $http({
                method : 'POST',
                url: 'https://api.myserver.net/oauth/token',
                data: 'username=' + viewModel.userName + '&password=' + viewModel.password + '&grant_type=password',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                }
            }).then(_requestAccessTokenSuccess, _requestAccessTokenError);
        };

        // This method contacts the API endpoint the schedule an OR suite
        viewModel.scheduleORSuite = function() {
            viewModel.requestAccessToken();
            if (viewModel.accessToken) {
                return; // viewModel.accessToken is null. Exit the method
            }
            $http({
                method : 'POST',
                url: 'https://api.myserver.net/api/scheduleOrSuite',
                data : angular.toJson(viewModel.form),
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': 'Bearer ' + viewModel.accessToken
                }
            }).then(_scheduleORSuiteSuccess, _scheduleORSuiteError);
        };

        // Called when request for access token is successful
        function _requestAccessTokenSuccess(response) {
            viewModel.tokenDisplay = response.data;
        };

        // Called when request for access token results in error
        function _requestAccessTokenError(response) {
            viewModel.tokenDisplay = 'An error occured: ' + response.status;
        };

        // Called when scheduling of operating suite is successful
        function _scheduleORSuiteSuccess(response) {
            viewModel.accessToken = response.data.access_token;
        };

        // Called when scheduling of operating suite results in error
        function _scheduleORSuiteError(response) {
            viewModel.tokenDisplay = 'An error occured: ' + response.data;
        };
    });
</script>

Here is the HTML form that makes use of the controller.

<form ng-submit="viewModel.scheduleORSuite()" novalidate>
...
...
</form>

Is there a way to make the second method (scheduleORSuite) wait until the first method (requestAccessToken) completes? The access token is required in order to call the API to schedule an OR suite.

3 Answers 3

1

$http is an async method, so you need to use callbacks

var scheduleORSuiteApp = angular.module('scheduleORSuiteApp', []);

scheduleORSuiteApp.controller('ScheduleORSuiteController', function($scope, $http) {
  var viewModel = this;
  viewModel.accessToken = null;
  viewModel.userName = 'theUserName';
  viewModel.password = 'thePassword';

  viewModel.requestAccessToken = function() {
    viewModel._requestAccessToken().then(_requestAccessTokenSuccess, _requestAccessTokenError);
  };

  viewModel.scheduleORSuite = function() {
    if (viewModel.accessToken) {
      viewModel._scheduleORSuite.then(_scheduleORSuiteSuccess, _scheduleORSuiteError);
    } else {
      viewModel._requestAccessToken().then(function(response) {
        viewModel.tokenDisplay = response.data;
        viewModel._scheduleORSuite.then(_scheduleORSuiteSuccess, _scheduleORSuiteError);
      }, _requestAccessTokenError);
    }
  };

  // This method contacts the API endpoint the schedule an OR suite
  viewModel._scheduleORSuite = function() {
    return $http({
      method: 'POST',
      url: 'https://api.myserver.net/api/scheduleOrSuite',
      data: angular.toJson(viewModel.form),
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer ' + viewModel.accessToken
      }
    });
  };

  // This method requests the access token
  viewModel._requestAccessToken = function() {
    return $http({
      method: 'POST',
      url: 'https://api.myserver.net/oauth/token',
      data: 'username=' + viewModel.userName + '&password=' + viewModel.password + '&grant_type=password',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    })
  };

  // Called when request for access token is successful
  function _requestAccessTokenSuccess(response) {
    viewModel.tokenDisplay = response.data;
  };

  // Called when request for access token results in error
  function _requestAccessTokenError(response) {
    viewModel.tokenDisplay = 'An error occured: ' + response.status;
  };

  // Called when scheduling of operating suite is successful
  function _scheduleORSuiteSuccess(response) {
    viewModel.accessToken = response.data.access_token;
  };

  // Called when scheduling of operating suite results in error
  function _scheduleORSuiteError(response) {
    viewModel.tokenDisplay = 'An error occured: ' + response.data;
  };
});

Sign up to request clarification or add additional context in comments.

4 Comments

Why did you have two methods, viewModel._requestAcctionToken and viewModel.requestAccessToken?
when you call viewModel._requestAcctionToken it returns a promise that you can use for next operation, but viewModel.requestAccessToken internally uses viewModel._requestAccessToken and sets the viewModel.accessToken value and returns nothing. it is useful if you just want to get the token.
OK, I think I see. So viewModel.requestAccessToken is not necessary but more there as a method that can be called just to get the access token and that's it?
exactly. also unlike the other answers posted here, viewModel.scheduleORSuite will only calls the oauth/token once and saves the token for all subsequent posts.
1

It's because your requestAccessToken() method contains a promise, and JavaScript won't wait for this to complete before carrying on with the rest of the code.

The best thing to do would be to return the promise from requestAccessToken() for use in scheduleORSuite()

viewModel.requestAccessToken = function () {
    return $http({
        method : 'POST',
        url: 'https://api.myserver.net/oauth/token',
        data: 'username=' + viewModel.userName + '&password=' + viewModel.password + '&grant_type=password',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        }
    });
};

viewModel.scheduleORSuite = function() {
    viewModel.requestAccessToken().then(function(response) {
        viewModel._requestAccessTokenSuccess(response);

        if (viewModel.accessToken) {
            return; // viewModel.accessToken is null. Exit the method
        }
        $http({
            method : 'POST',
            url: 'https://api.myserver.net/api/scheduleOrSuite',
            data : angular.toJson(viewModel.form),
            headers: {
                'Content-Type': 'application/json',
                'Authorization': 'Bearer ' + viewModel.accessToken
            }
        }).then(_scheduleORSuiteSuccess, _scheduleORSuiteError);
    }, _requestAccessTokenError);
};

I've also noticed though that the variable you're checking for your access token, viewModel.accesstoken, is being set until the _scheduleORSuiteSuccess() function? I'm presuming this is an error and it should be set in _requestAccessTokenSuccess() instead?

1 Comment

Would you be able to access _requestAccessTokenSuccess using viewModel._requestAccessTokenSuccess(response)? It is not attached to the view model.
0

What you want todo is make sure the request returns with the access token first, then make the second request. Todo that, use a promise. the $q library works well for this. See viewModel.requestAccessTokenfor the changes I made.

<script type="text/javascript">
var scheduleORSuiteApp = angular.module('scheduleORSuiteApp', []);

scheduleORSuiteApp.controller('ScheduleORSuiteController', function($scope, $http, $q) {
  var viewModel = this;
  viewModel.accessToken = null;
  viewModel.userName = 'theUserName';
  viewModel.password = 'thePassword';

  // This method requests the access token
  viewModel.requestAccessToken = function() {
    var defer = $q.defer();

    $http({
      method: 'POST',
      url: 'https://api.myserver.net/oauth/token',
      data: 'username=' + viewModel.userName + '&password=' + viewModel.password + '&grant_type=password',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    }).then(function(response) {
      viewModel.tokenDisplay = response.data;
      defer.resolve(); //fullfills the promise
    }, function(err) {
      viewModel.tokenDisplay = 'An error occured: ' + response.status;
      defer.reject(); //rejects the promise
    });

    return $q; //returns a promise
  };

  // This method contacts the API endpoint the schedule an OR suite
  viewModel.scheduleORSuite = function() {
    viewModel.requestAccessToken().then(function() {
      //On fullfillment of the promise from requestAccessToken...

      if (viewModel.accessToken) {
        return; // viewModel.accessToken is null. Exit the method
      }
      $http({
        method: 'POST',
        url: 'https://api.myserver.net/api/scheduleOrSuite',
        data: angular.toJson(viewModel.form),
        headers: {
          'Content-Type': 'application/json',
          'Authorization': 'Bearer ' + viewModel.accessToken
        }
      }).then(_scheduleORSuiteSuccess, _scheduleORSuiteError);
    }, function() {
      //Error occured in requestAccessToken();
    })
  };


  // Called when scheduling of operating suite is successful
  function _scheduleORSuiteSuccess(response) {
    viewModel.accessToken = response.data.access_token;
  };

  // Called when scheduling of operating suite results in error
  function _scheduleORSuiteError(response) {
    viewModel.tokenDisplay = 'An error occured: ' + response.data;
  };
});
</script>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.