As a newcomer to AngularJS and ui-router, I am currently facing the challenge of handling 404 errors for resources not found. My goal is to display an error message without changing the URL in the address bar.
Here is how I have configured my states:
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"
});
}
]);
(Although I'm using TypeScript, I converted the above to pure JavaScript)
If, for example, I visit the following URL:
http://localhost:12345/listings/bef8a5dc-0f9e-4541-8446-4ebb10882045
It should navigate to the listings.details state.
However, if that particular resource does not exist and the promise from the resolve function fails with a 404 error, I handle it as follows:
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 });
}
});
}
]);
The objective here is to transition to the listings.notFound state while keeping the destination URL unchanged in the address bar. Using a relative path allows for reusing this logic for other resources.
Unfortunately, I encounter an exception:
Path '^.notFound' not valid for state 'listings.details'
This issue arises because the toState argument provided by the $stateChangeError event lacks information about its parent; specifically, toState.parent is undefined. Trying
relative: $state.get(toState.name)
also proves ineffective since ui-router once again returns state.self
.
I prefer to avoid maintaining a list of absolute paths and repeat the navigation logic for the state hierarchy. Is there a different, more appropriate way to handle 404 errors? If not, what would be the best approach?