1

When my backend sends me a 404 error (URL is valid, just because the resource is not found, like http://localhost:4200/post/title-not-exist), I want Angular redirect to my NotFoundComponent, without changing the URL in browser.

Code below (simplified):

constructor(private router: Router, private location: Location) {}

handleError(err: HttpErrorResponse) {
  switch (err.status) {
    case 404:
      url = this.router.routerState.snapshot.url;
      // '**' is set in the app-routing.module.ts
      // { path: '**', component: NotFoundComponent }
      this.router.navigate(['**'], { replaceUrl: false });
      // weird here too
      // without the setTimeout(), replaceState will not work
      setTimeout(() => {
        this.location.replaceState(url);
      });
      break;

    default:
      break;
  }
  return throwError(err);
}

Now, I can redirect to NotFoundComponent and the URL not changed, the problem is:

  1. My history will become things like:
    /post/title-exist/post/not-exist/post/not-exist
    which should be
    /post/title-exist/post/not-exist
    And the go back function will stuck here.
  2. Without the setTimeout(), location.replaceState() will not work, the URL in browser becomes /** and not changing to the URL that snapshotted before.
funkid
  • 577
  • 1
  • 10
  • 30
  • Can you reproduce this in stackblitz ? – dota2pro May 25 '19 at 14:36
  • @dota2pro I have to send a mock 404 status from backend, can stackblitz do that? – funkid May 25 '19 at 14:47
  • https://angular.io/api/http/testing/MockBackend – dota2pro May 25 '19 at 14:59
  • you can also create a mock api online https://www.mockable.io/a/#/try or just pass a json object after a timeout ?????? what is the need of an API for demo – dota2pro May 25 '19 at 15:01
  • What dont you just hide/show NotFoundComponent based on the API return status, instead of using the router? And when you show that component, hide your `` – David May 25 '19 at 16:11
  • @David you mean by using `*ngIf` to hide/show component? Does that mean I have to make my `post.component.html` become ``? But I think it's not a good practice since I may have to add `NotFoundComponent` to these components manually. – funkid May 26 '19 at 02:44
  • I assumed that the `NotFoundComponent` was the same for the whole application. So when your service returns a 404, your `PostComponent` can catch that error, broadcast a message to tell your root component to show the `NotFoundComponent` and hide the `` outlet. You could even do it in a more generic way where an interceptor does that for all your API requests – David May 27 '19 at 09:19

1 Answers1

4

Update

Use skipLocationChange to keep the original URL, replaceUrl is not needed anymore.
Reference here.

Original

The key point is to change the replaceUrl in this.router.navigate from false to true, like this:

this.router.navigate(['**'], { replaceUrl: true });

Logic behind:

  • When replaceUrl is false, you are pushing a new history to your browser history stack. So the history stack becomes like this:

/post/title-exist/post/title-not-exist/**

And with the help of replaceState(), it changes the /** history to /post/title-not-exist, which makes the history finally becomes:

/post/title-exist/post/title-not-exist/** /post/title-not-exist

  • When replaceUrl is true, you are directly modifying the current history stack, which makes your browser history becomes like this:

/post/title-exist/post/title-not-exist /**

Then, with the help of replaceState(), the history becomes:

/post/title-exist/post/title-not-exist /** /post/title-not-exist

funkid
  • 577
  • 1
  • 10
  • 30
  • But I'm still confusing about why `location.replaceState()` will not work without `setTimeout()`. Don't know if it's related with [this](https://stackoverflow.com/a/39079352/10684507) answer. – funkid May 26 '19 at 04:12