Dealing with an endless loop caused by a promise in AngularJS's ui router $stateChangeStart event

I am currently working on implementing authentication in my Angular application and I want to redirect to an external URL when a user is not logged in (based on a $http.get request).

However, I seem to be stuck in an infinite loop when using event.preventDefault() as the first line in the $stateChangeStart function.

I have come across various solutions on Stack Overflow suggesting to place event.preventDefault() just before the state.go in the else statement. But this results in the controllers being triggered and the page being displayed before the promise is returned.

Even when I move the event.preventDefault() to the else block, something strange happens:

When navigating to the root URL, it automatically adds /#/ after the URL and triggers $stateChangeStart multiple times.

This is how the run part of app.js looks like:

.run(['$rootScope', '$window', '$state', 'authentication', function ($rootScope, $window, $state, authentication) {
    $rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
        event.preventDefault();
        authentication.identity()
        .then(function (identity) {
             if (!authentication.isAuthenticated()) {
                 $window.location.href = 'external URL';
                 return;
            } else {
                 $state.go(toState, toParams);
            }
        });
    });
}]);

The identity() function in authentication.factory.js:

function getIdentity() {
    if (_identity) {
        _authenticated = true;
        deferred.resolve(_identity);
        return deferred.promise;
    }

    return $http.get('URL')
    .then(function (identity) { 
        _authenticated = true;
        _identity = identity;
        return _identity;
    }, function () {
        _authenticated = false;
    });
}

EDIT: Here are the states defined:

$stateProvider
    .state('site', {
        url: '',
        abstract: true,
        views: {
            'feeds': {
                templateUrl: 'partials/feeds.html',
                controller: 'userFeedsController as userFeedsCtrl'
            }
        },
        resolve: ['$window', 'authentication', function ($window, authentication) {
            authentication.identity()
            .then(function (identity) {
                if (!authentication.isAuthenticated()) {
                    $window.location.href = 'external URL';
                }
            })
        }]
    })
    .state('site.start', {
        url: '/',
        views: {
            'container@': {
                templateUrl: 'partials/start.html'
            }
        }
    })
    .state('site.itemList', {
        url: '/feed/{feedId}',
        views: {
            'container@': {
                templateUrl: 'partials/item-list.html',
                controller: 'itemListController as itemListCtrl'
            }
        }
    })
    .state('site.itemDetails', {
        url: '/items/{itemId}',
        views: {
            'container@': {
                templateUrl: 'partials/item-details.html',
                controller: 'itemsController as itemsCtrl'
            }
        }
    })
}])

If you require additional information or code snippets from app.js, please let me know!

Answer №1

When using $stateChangeStart, your promise does not have to be resolved before exiting the current state. To make the state wait for a promise, you can utilize the <code>resolve
function within the state's options.

.config(function($stateProvider) {
  $stateProvider.state('home', {
    url: '/',
    resolve: {
      auth: function($window, authentication) {
        return authentication.identity().then(function (identity) {
           if (!authentication.isAuthenticated()) {
             $window.location.href = 'external URL';
           }
        });
      }
    }
  });
});

By returning a promise from this function, ui-router will only initialize the state once that promise is successful.

If there are other states or child states dependent on this promise, you must inject auth into them as well.

As mentioned in the wiki:

In order to wait for promises to be resolved before instantiating the children states, the resolve keys MUST be injected into the child states.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How can we automatically redirect users to an external website based on their responses to a specific question in SurveyJS?

Within my SurveyJS json, I have a radiogroup question that prompts users to make a choice. If they select a specific option, I'd like to redirect them to a different page. Otherwise, I want them to continue with the survey. Is this achievable? (The s ...

Updating the footer of a React application using Material UI Grid

Having some trouble customizing the footer in a React Material-UI grid. Refer to the image below for details. Any ideas on how to change the: 1 row selected? Thank you! ...

Passing a parameter from a redirect function to an onClick in React using TypeScript

I am facing a challenge in passing a parameter to my redirectSingleLocker function. This function is intended to take me to the detailed page of a specific locker, identified by a guid. The lockerData.map method is used to display all the JSON data in a ta ...

The Vuetify navigation drawer seems to have a quirk where it only closes after an item

I am brand new to Vue and struggling to figure out why my vue v-navigation-drawer is not working properly. It is located in app-root.vue and was initially closing when clicking on a drawer item, but now requires two clicks to close. After the first click, ...

Issue with the dynamic updating of props

Every time a radio button is clicked within Test.js, the handleclick function executes, updating an array. However, the issue lies in not sending this updated array back to graph_test.js. As a result, graph_test.js only receives the initial array filled wi ...

Need to `come back` multiple times

I am facing an issue where I want to return multiple lines, but only the first line is being returned. I attempted to create a function specifically for returning the line, but encountered errors because I couldn't figure out where to place it. Does ...

Steps for serving a "noop" document via HTTP

I am in the process of creating a CGI script that performs various functions. I am striving to maintain simplicity and portability within the script. My main objective is to provide users with a way to send a message to the server without losing the curren ...

Generating JSON objects within the Ionic SQLite framework

I am new to Ionic development and I'm looking for a way to convert a JSON string into a JSON object in Ionic, as well as how to access this JSON data on an HTML page. controller.js app.controller('OilTrackerListingCntrl', function($scope, ...

Images cannot be uploaded using ajax

I have been troubleshooting an issue with uploading image files using ajax without refreshing the page. However, I am facing a problem where the file does not get moved to the specified folder even after implementing basic PHP code for file upload. if($_S ...

Using jQuery to modify the CSS of a div located outside of an iframe

Currently, I am developing a straightforward script. Firstly, here is all of the code that I have: <script src="//code.jquery.com/jquery-1.10.2.js"></script> <script type="text/javascript"> function SuperWebF1() { $("#outerd ...

Is there a way for me to adjust the image dimensions so that it doesn't surpass the width of its parent container?

When working with images, it can be tricky to set the original width while also ensuring it fits within a parent container. For example, if the parent container has a width of 1000px, you may want the image to have a max-width of 100%, but not exceed 1000p ...

Sorting data within an Angular directive using a filtering service

I have created a custom directive called <jobs type="1" /> to display a specific set of data based on the attribute value. The 'type' attribute determines which type of job the user should see, and I'm using the following code in my te ...

What are the steps to resolve the issue "Error: no valid exports main found" specifically on a Windows 7 operating system?

I've been encountering an issue while attempting to run my react app on Windows 7 OS. I have npm version 6.13.4 and node version 13.6.0 installed on my system. Every time I try to start the application using npm start, I receive the following error co ...

Converting the AppDelegate.m file to AppDelegate.mm and updating the AppDelegate.h file during the transition from React Native 0.66.4 to 0.71.3

Original code in AppDelegate.h: #import <React/RCTBridgeDelegate.h> #import <UIKit/UIKit.h> #import <UserNotifications/UserNotifications.h> @interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, UNUserNotificat ...

Is it appropriate to utilize response headers (specifically 400 error codes) to communicate errors, especially when working with x-editable?

Exploring the capabilities of the plugin reveals that it offers two distinct callbacks upon posting a result: error and success. The error callback is triggered in cases where the server does not respond with a 200 header. This means that if the server d ...

What is the process for assigning specific tags to specific items within an array?

As I navigate through a list of students, I am encountering an issue with my tag functionality. Currently, when a student adds a tag to their container, it is being added to every student's tags instead of just the intended student. How can I modify t ...

Utilizing a window.onload function in Microsoft Edge

After trying to run some code post-loading and rendering on the target page, I was recommended to use the Window.load function. This method worked flawlessly in Firefox and Chrome, but unfortunately, I couldn't get it to function in IE. Is there an al ...

AngularJS UI router regular expressions allows for dynamic and flexible routing

I'm attempting to find a parameter with two possible values: 'current' or a number with at least 10 digits. Here's what I've tried: url: '/history/{code:^current$|^[0-9]{10,}$}' Although this regular expression suc ...

What steps do I need to take to integrate AngularJS with the ServiceStack FallbackRoute attribute in order to enable support for HTML5 pushstate URLs?

Currently, I am in the process of developing a client/server solution which involves using an AngularJS Single Page App as the client component and a Self-Hosted ServiceStack RESTful API as the server component. The setup consists of HTML and JavaScript fi ...

What is the best way to display multiple files in a vertical stack when choosing a file?

<div class="selected-file-container align-items-center justify-content-between d-none"> <div class="selected-file d-flex align-items-center"> <img id="selectedImage" class="hidden" src="&qu ...