Naomi
Naomi

Reputation: 718

Struggling with asynchronous nature of AngularJs JavaScript

I have the following code

if (testNavigation() && $scope.selections.somethingChanged) {
    return false;
}

in the testNavigation I am calling modal dialog and if I answer Ok in that dialog, I re-set somethingChanged to false. My problem is that when the code is executed, the testNavigation and modal dialog is by-passed and executed later and therefore my test is not working as I need it to work. What should I change in order for my logic to properly work, e.g. first invoke my modal dialog, and proceed accordingly in the main function?

This is my testNavigation method:

var testNavigation = function()
            {                
                if ($scope.selections.somethingChanged) {
                    
                    var modal = $modal.open({
                        controller: function ($scope) {
                            $scope.okClass = 'btn-primary',
                            $scope.okLabel = resourceFactory.getResource('Labels', 'yes'),
                            $scope.cancelLabel = resourceFactory.getResource('Labels', 'cancel'),
                            $scope.title = resourceFactory.getResource('Labels', 'unsavedChanges'),
                            $scope.message = resourceFactory.getResource('Messages', 'unsavedChanges');
                        },
                        templateUrl: '/app/templates/modal'
                    });

                    modal.result.then(function () {
                        
                        $scope.selections.somethingChanged = false;                      
                        
                    });
                }
                
                return true;
            }

I'll try to add more details. I have LoadView() and New() functions in the Index page controller. In both of these functions I need to do the following:

if $scope.selections.somethingChanged = false I need to proceed with the logic.

if $scope.selections.somethingChanged = true I need to pop up a modal dialog asking if I want to go ahead and Cancel my changes or Stay on the current page. If I answer Yes, I want to go ahead with the logic.

So, that's the purpose of the separate testNavigation function. In the languages where each function call is sequential, that would work as I intended. But it doesn't work this way in AngularJS / JavaScript and I am not sure how to make it to work the way I need. We tried few ideas with $q service but didn't get the result.

Upvotes: 4

Views: 155

Answers (2)

pdenes
pdenes

Reputation: 802

Make testNavigation() always return a promise (either with the result of the modal, or with false straight away, when somethingChanged is false and you don't want to ask the question. Proceed when this promise is resolved:

var testNavigation = function() {                
    if ($scope.selections.somethingChanged) {        
        var modal = [...]
        return modal.result;  // returns a promise that will be resolved when the modal is closed
    } else {
        return $q.when(true); // returns a promise that will be resolved straight away with true
    }
}

// when used:

testNavigation().then(function() {
    ...do stuff...
})

Upvotes: 2

Andrew Luhring
Andrew Luhring

Reputation: 1894

Without knowing what your test looks like, this is kind of difficult, but from what it looks like, you're creating a race condition.

You call testNavigation() which always returns true, but because $scope.selections.somethingChanged is set to false at some point in the future, $scope.selections.somethingChanged may not finish before the end of that evaluation- so while you're setting $scope.selections.somethingChanged to false in testNavigation, it may or may not be false when the second if is performed:

if( testNavigation() &&  // returns true no matter what.
    $scope.selections.somethingChanged // this is changed with a promise within testNavigation. that promise could either complete or not complete before this is evaluated with the if.
  ){
    return false;
}

var testNavigation = function() {
  if ($scope.selections.somethingChanged) {

    var modal = $modal.open({
      // details
    });

    modal.result.then(function() {
      // this is async and could take place any time between {locationA} and never.
      $scope.selections.somethingChanged = false;
    });
  //{locationA}
  }

  return true;
}

I can imagine that producing some weird results in tests.

Upvotes: 0

Related Questions