9

I needed to cancel the route on promise rejection and stay on the current page without redirecting to default/error page, I have tried different ways of rejecting the promise but ended up resolving the route or redirected to the default route.

@Injectable()
export class ModelResolver implements Resolve<MyModel> {
    constructor(private router: Router) {
    }

    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<any> {
        let params: any = route.params;

        let model: MyModel= new MyModel();


        return model.init().then((obj: MyModel) => {
                    return obj;
                }).catch((err: any) => {
                    Promise.reject({message: err , ngNavigationCancelingError: true});
            });
    }

}

reject({message: err , ngNavigationCancelingError: true}); // redirects to my default route

return Observable.throw({message: err , ngNavigationCancelingError: true}); // redirects to the current route without cancelling

resolve(null); or return Observable.empty() // redirects to the current route without cancelling

Pradeep Reddy
  • 113
  • 1
  • 1
  • 9
  • have you looked at [CanDeactivate: handling unsaved changes?](https://angular.io/guide/router#candeactivate-handling-unsaved-changes) – Madhu Ranjan Jan 23 '18 at 19:31
  • I need the data to be resolved and then decide whether to navigate or not. https://stackoverflow.com/questions/42652708/angular2-exploring-resolved-data-in-canactivate-guard – Pradeep Reddy Jan 31 '18 at 06:48
  • OK, I assume you will have a service call to get the data in Rote Render, can you not call the same service from CanDeactivate? ideally you should have all the params which you may need for the service call in CanDeactivate ? – Madhu Ranjan Jan 31 '18 at 15:11
  • CanDeactivate can be used while leaving from a route , to check/notify any unsaved data. CanActivate gets executed before Data Resolving and i can move this call to CanActivate but it doesn't persist the data as mentioned in this [issue](https://stackoverflow.com/questions/42652708/angular2-exploring-resolved-data-in-canactivate-guard) – Pradeep Reddy Feb 01 '18 at 12:20

5 Answers5

6

In order to cancel route from resolver, you can use observable instead of promise and call: observer.complete() without calling: observer.next()

For example:

 resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot){

   return new Observable((observer) => {
     let somePromiseOrObservableToGetDate=?????;

     somePromiseOrObservableToGetDate.then(data => {
       if (data) {
         observer.next(1);
       }
       observer.complete();
     })
       .catch((err: any) => {
         observer.complete();
       });
   });

 } 

and in your example:

let model: MyModel = new MyModel();

return new Observable((observer) => {
  model.init().then((obj: any) => {
    observer.next(obj);
    observer.complete();
  })
    .catch((err: any) => {
      observer.complete();
    });
});
Jeff Schaller
  • 2,352
  • 5
  • 23
  • 38
M Barzel
  • 739
  • 7
  • 9
  • 3
    Has this behaviour possibly changed in Angular 6? Navigation still occurs for me with this code. – Alex Peters Sep 11 '18 at 09:31
  • Having the same issue like @AlexPeters, above method just continue navigation while not resolving the actual value. Any ideas on this? – mitschmidt Feb 10 '19 at 10:41
  • 1
    This article is about route guards, not data `Resolve` guards. Unfortunately doesn't look like they added the same functionality there. – Simon_Weaver Mar 04 '19 at 03:12
3

Using Angular 6 the only way I could prevent the navigation from happening was throwing an unhandled error from the resolver stream. The simplest way to test this out:

resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    // throwError imported from 'rxjs'.
    return throwError('some error object');
}

The navigation would also not happen if you'd return a stream that never completes (ie. NEVER from 'rxjs') but then the router would keep on waiting for the stream to either complete or error (which never happens) meaning all routing operations will stop working.

UPDATE 2021:

This is not found in the resolver docs but Angular 10 upgrade guide mentions that:

Any resolver which returns EMPTY will cancel navigation. If you want to allow navigation to continue, you will need to update the resolvers to emit some value, (i.e. defaultIfEmpty(...), of(...), etc).

Tried it out with EMPTY and this indeed works now. There's no longer a need to throw an error to cancel the navigation.

Rene Juuse
  • 575
  • 8
  • 16
1

One way to cancel navigation is to complete an Observable without returning any data. I couldn't find a documentation on this behavior, but seems to be working.

resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> {
    let params: any = route.params;
    let model: MyModel= new MyModel();
    return Observable.fromPromise(model.init()).pipe(
        catchError((err: any) => Observable.empty());
}
Md Enzam Hossain
  • 1,246
  • 1
  • 11
  • 12
1

You can return the EMPTY observable within the resolve method. Below is an example of a resolve method that cancels navigation

  resolve(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<T> | Observable<T> {

    return EMPTY;
    
  }

Note: EMPTY is imported from rxjs

Priest Sabo
  • 353
  • 2
  • 6
0

Use this work around to reject and stay on the same page for route resolver.

resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<any> {

        return new Promise((resolve, reject) => {
            return model.init().then((obj: any) => {
                resolve(obj);
            }).catch((err: any) => {
                this.router.navigateByUrl(this.location.path());
                resolve(false);
            });
        });

    }