3

This is a follow up to Angular 4 + zonejs: routing stops working after uncaught error since we had a hard time integration the suggested changes into our project.

The reason for this can be seen in this adapted plunker https://embed.plnkr.co/oUE71KJEk0f1emUuMBp8/ in app.html:

The routerLinks in our project were not prefixed with a slash "/". This breaks the whole navigation once you navigate to another section after visiting the "Error Component". All links are being rewritten with the current path, e.g. home.

Adding the slash in the routerLink attributes fixes this behaviour.

Why is that?

And is there some documentation/spec concerning this?

We only found this angular ticket and the api for RouterLink-directive which says

or doesn't begin with a slash, the router will instead look in the children of the current activated route.

But how is this related to what is happening with an uncaught error respectively with the suggested workaround from the previous question?

Community
  • 1
  • 1
Benjamin Seiller
  • 2,875
  • 2
  • 32
  • 42

1 Answers1

1

After an navigation error happens the routing state is restored

this.currentRouterState = storedState;
this.currentUrlTree = storedUrl;

https://github.com/angular/angular/blob/4.1.2/packages/router/src/router.ts#L750-L751

After that within executing createUrlTree the startPosition is obtained:

function findStartingPosition(nav, tree, route) {
    if (nav.isAbsolute) { // it will be executed when you use `/` in routeLink
        return new Position(tree.root, true, 0);
    }
    if (route.snapshot._lastPathIndex === -1) { // without '/'
        return new Position(route.snapshot._urlSegment, true, 0);
    }
    ...
}

As we can see in code above when you use slash in routeLinks then router will create position based on tree.root which has not changed.

then it is used for creating UrlTree (oldSegmentGroup in code below)

function tree(oldSegmentGroup, newSegmentGroup, urlTree, queryParams, fragment) {
    ...
    if (urlTree.root === oldSegmentGroup) { // will be false after the error
        return new UrlTree(newSegmentGroup, qp, fragment);
    }
    return new UrlTree(replaceSegment(urlTree.root, oldSegmentGroup, newSegmentGroup), qp, fragment);
}

So workaround might be as follows:

We no longer need RouteReuseStrategy.

We store errored state

let erroredUrlTree;
let erroredState;

export class AppModule {
  constructor(private router: Router) {
    router.events.subscribe(function (e) {
      if(e instanceof  NavigationError ) {
        erroredState = (router as any).currentRouterState;
        erroredUrlTree =  (router as any).currentUrlTree;
      }
    });
  }
}

and recovery it after error occurs:

@Injectable()
export class MyErrorHandler implements ErrorHandler {
  constructor(private inj: Injector) {}

  handleError(error: any): void {
    console.log('MyErrorHandler: ' + error);
    if(erroredUrlTree) {
      let router: any = this.inj.get(Router);
      router.currentRouterState = erroredState;
      router.currentUrlTree = erroredUrlTree;
      erroredState = null;
      erroredUrlTree = null;
    }
  }
}

Modified Plunker

It looks terrible but maybe it will help to understand what the problem is

yurzui
  • 205,937
  • 32
  • 433
  • 399