Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS / ui-router: handle 404 on resolve

I'm a beginner with AngularJS and ui-router, and am trying to handle 404s on resources not found. I would like an error to be displayed, without changing the URL in the address bar.

I have configured my states as such:

app.config([
    "$stateProvider", function($stateProvider) {
        $stateProvider
            .state("home", {
                url: "/",
                templateUrl: "app/views/home/home.html"
            })
            .state("listings", {
                abstract: true,
                url: "/listings",
                templateUrl: "app/views/listings/listings.html"
            })
            .state("listings.list", {
                url: "",
                templateUrl: "app/views/listings/listings.list.html",
            })
            .state("listings.details", {
                url: "/{id:.{36}}",
                templateUrl: "app/views/listings/listings.details.html",
                resolve: {
                    listing: [
                        "$stateParams", "listingRepository",
                        function($stateParams, repository) {
                            return repository.get({ id: $stateParams.id }).$promise;
                        }
                    ]
                }
            })
            .state("listings.notFound", {
                url: "/404",
                template: "Listing not found"
            });
    }
]);

(I'm actually using TypeScript, but I tried to change the above to be pure JavaScript)

If, say, I navigate to the following url: http://localhost:12345/listings/bef8a5dc-0f9e-4541-8446-4ebb10882045 That should open the listings.details state. However if that resource does not exist, the promise returned from the resolve function will fail with a 404, which I catch here:

app.run([
    "$rootScope", "$state",
    function($rootScope, $state) {
        $rootScope.$on("$stateChangeError", function(event, toState, toParams, fromState, fromParams, error) {
            event.preventDefault();
            if (error.status === 404) {
                $state.go("^.notFound", null, { location: true, relative: toState });
            }
        });
    }
]);

What I'm trying to do here is to go to the listings.notFound state, without changing the destination URL in the address bar. I use a relative path because I would like to re-use this logic for other resources.

However, I get an exception:

Path '^.notFound' not valid for state 'listings.details'

This error happens because the toState argument given by the $stateChangeError event does not know its parent, i.e. toState.parent is undefined. In the transitionTo function of ui-router, I can see that the object given as argument is to.self, which only provides a subset of the information. Using relative: $state.get(toState.name) also doesn't help, because internally ui-router once again returns state.self

I would like to avoid maintaining a list of absolute paths, and rewrite the logic for navigating in the state hierarchy (no matter how simple it is, DRY and all that).

Am I going about it wrong, is there another proper way of handling 404? If not, what is the best approach?

like image 824
Antoine Avatar asked Jul 21 '14 10:07

Antoine


1 Answers

It's better to use $urlRouterProvider to handle this exceptions

app.config([
    "$stateProvider", "$urlRouterProvider", function ($stateProvider, $urlRouterProvider) {
        $urlRouterProvider.otherwise('/404');
        // define states        
}]);
like image 196
Umidbek Karimov Avatar answered Oct 04 '22 15:10

Umidbek Karimov