Skip to content

Infinite loop when redirecting to state from $stateChangeError handler #898

@albv

Description

@albv

Here is a minimal example I made to reproduce my issue:

angular.module('routerTestApp', ['ui.router'])

    .config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {

        $urlRouterProvider.otherwise('/state/a');

        $stateProvider.
            state('state', {
                url: '/state',
                abstract: true,
                resolve: {
                    parentResolved: ['$timeout', function ($timeout) {
                        return $timeout(function () {
                            return [];
                        }, 1000);
                    }]
                }
            }).

            state('state.a', {
                url: '/a',
                resolve: {
                    resolved: ['$q', 'parentResolved', function ($q, parentResolved) {
                        return $q.reject({ redirect: 'state.b' });
                    }]
                }
            }).

            state('state.b', {
                url: '/b'
            });

    }])

    .run(['$rootScope', '$exceptionHandler', '$state', function ($rootScope, $exceptionHandler, $state) {
        $rootScope.$on('$stateChangeError', function (e, toState, toParams, fromState, fromParams, error) {
            if (error && error.redirect) {
                return $state.go(error.redirect);
            }

            $exceptionHandler(error);
        });
    }]);

The problem is when going to any non-existent url such as '/abc' for example - app falls to infinite 'location change' loop. Url should be pasted directly to browser address string because if previously we entered some valid state all subsequent redirects would work as expected.

By 'location change' loop I mean following:

  1. $urlRouterProvider.otherwise('/state/a') send us to 'state.a'
  2. 'state.a' resolve failed so $stateChangeError handler is called
  3. handler redirects us to 'state.b'
  4. but syncUrl() function that called in ui-router after firing $stateChangeError sees that currentLocation (it's '/abc' at that moment) doesn't match $location.url() and sends us back to '/abc' - so we are returning to point 1).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions