Change ui-view template without changing URL

2019-08-03 07:18发布

问题:

I have the following states configuration (simplified):

$stateProvider
  .state(
    'showitem', 
     { url: '/item/{id}', templateUrl: 'showitem.html', controller: 'MyCtrl' }
  )
  .state(
    'notFound',
    { url: '^*path', templateUrl: '404.html'}
  );

When I enter /badurl, 404 error is shown as expected.

When I enter /item/123, the application's API is being queried for item with specified identifier. It returns item data on success or 404 HTTP header if the item could not be found. In order to detect such errors globally, I wrote a http interceptor:

$httpProvider.interceptors.push(function($q, $location) {
  return {
    responseError: function(rejection) {
      if(rejection.status == 404)
        $location.path('/404');
      return $q.reject(rejection);
    }
  };
});

Code works but when the item's id is wrong, the /item/123 URL changes to /404 which shows an error page.

Question is - how to load the 404.html view into my <div ui-view></div> element without changing the URL?

There is a similar question but the templateProvider does not seem to address my problem.

回答1:

Solution inspired by this answer and @RadimKohler comment. Remove url from notFound route:

$stateProvider
.state(
  'showitem', 
  { url: '/item/{id}', templateUrl: 'showitem.html', controller: 'MyCtrl' }
)
.state(
  'notFound',
  { templateUrl: '404.html'}
);

Configure oterwise rule:

$urlRouterProvider.otherwise(function($injector, $location) {
  var $state = $injector.get('$state');
  $state.go('notFound');
  return $location.path();
});

And finally, go to the notFound route on 404 API error:

$httpProvider.interceptors.push(function($q, $injector) {
  return {
    responseError: function(rejection) {
      if(rejection.status == 404)
        $injector.get('$state').go('notFound');
      return $q.reject(rejection);
    }
  };
});