54

I'm new to angular, I want stop the routing when user clicks on refresh button or back button based on some condition. I don't know whether this is possible, if anybody knows help me

constructor(private route: Router) {
    this.route.events
        .filter(event => event instanceof NavigationEnd)
        .pairwise().subscribe((event) => {
            if (expression === true){
                // stop routing 
            } else {
                // continue routing
            }      
        });
}

Can it be possible? If yes, how can I do this?

ROMANIA_engineer
  • 51,252
  • 26
  • 196
  • 186
Gavishiddappa Gadagi
  • 1,010
  • 1
  • 16
  • 32

4 Answers4

49

I stumbled upon this question quite a bit after the fact, but I hope to help someone else coming here.

The principal candidate for a solution is a route guard.

See here for an explanation: https://angular.io/guide/router#candeactivate-handling-unsaved-changes

The relevant part (copied almost verbatim) is this implementation:

import { Injectable }           from '@angular/core';
import { Observable }           from 'rxjs';
import { CanDeactivate,
         ActivatedRouteSnapshot,
         RouterStateSnapshot }  from '@angular/router';

import { MyComponent} from './my-component/my-component.component';

@Injectable({ providedIn: 'root' })
export class CanDeactivateGuard implements CanDeactivate<MyComponent> {

  canDeactivate(
    component: MyComponent,
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean> | boolean {
    // you can just return true or false synchronously
    if (expression === true) {
      return true;
    }
    // or, you can also handle the guard asynchronously, e.g.
    // asking the user for confirmation.
    return component.dialogService.confirm('Discard changes?');
  }
}

Where MyComponent is your custom component and CanDeactivateGuard is going to be registered in your AppModule in the providers section and, more importantly, in your routing config in the canDeactivate array property:

{
  path: 'somePath',
  component: MyComponent,
  canDeactivate: [CanDeactivateGuard]
},
A. Chiesa
  • 6,154
  • 1
  • 24
  • 47
  • The downside is that we cannot apply guards globally to all routes without adding the guard to each and every route individually. – yankee May 31 '21 at 05:03
  • 2
    @yankee well, if you _really_ need to have your guard applied to every route, you can just preprocess the `ROUTES` constant before passing it into `RoutingModule.forRoot(ROUTES)`. A simple `ROUTES.foreach(r => r.canDeactivate = [ CanDeactivateGuard ])` or something similar would do. You could handle every other case (nested children routes, other guard types) with similar variations on this. – A. Chiesa May 31 '21 at 09:15
  • This does not work anymore. I am getting `NullInjectorError` did I did anything wrong? The only change I did, is that I registered my guard in `RouterModule.forChild` within lazy loaded module, where the component is used. – Akxe Nov 11 '21 at 23:18
  • @Akxe, this still works. I strongly suspect there is something wrong in your setup. The injector has everything to do with lazy loading, and probably nothing to do with guards. If you can't make it work, open another question! – A. Chiesa Nov 12 '21 at 09:06
  • In your answer, you never informed, that `CanDeactivateGuard` must be in `providers` array of a NgModule – Akxe Nov 12 '21 at 14:44
  • That doesn't seem to be very constructive. Every service in Angular (Guards are just a specialized service) need to be included in the Providers or being `providedIn: "root"`. I'm going to update the answer anyway, just in case anyone else had the same problem. – A. Chiesa Nov 12 '21 at 15:31
5

The easy way for me is with skiplocationchange in a new route navigate like this:

if(condition === true){

  const currentRoute = this.router.routerState;

  this.router.navigateByUrl(currentRoute.snapshot.url, { skipLocationChange: true });
  // this try to go to currentRoute.url but don't change the location.

}else{
  // do nothing;
}

is a no beautiful method but works

Javier López
  • 91
  • 1
  • 5
2

There is another solution which I invented and it works:

(For lazy people like me who do not want to create guard for handling this)

import { NavigationStart, Router } from '@angular/router';

In constructor:

constructor(private router: Router) {
    router.events.forEach((event) => {
      if (event instanceof NavigationStart) {
        /* write your own condition here */
        if(condition){
          this.router.navigate(['/my-current-route']); 
        }
      }
    });
  }

Hopefully you won't be lazy enough to change '/my-current-route' to your route url.

Hari Das
  • 9,108
  • 7
  • 52
  • 58
  • This is searching for unexpected problems, in my own experience. When a navigation is running, the router can get finicky in handling new navigation requests. IMHO this is the wrong kind of lazy. – A. Chiesa Oct 28 '21 at 06:17
  • 1
    I won't give a +1 on this one, but I liked the last sentence though. :D – Guillaume PETIT May 13 '22 at 06:42
  • My colleague left this gem. He's on a different project now. It didn't smell good, so I searched for another solution. I can bet he got it from here, as he is exactly the type that says "it's good because it works". Love the humor in the last sentence :D – bokkie May 20 '22 at 20:59
-8

You can use NavigationStart event like the following code snippet.

this.router.events.subscribe( (event: any) => {
   if (event instanceOf NavigationStart) {
      /// Your Logic
   }
})
KAUSHIK PARMAR
  • 419
  • 6
  • 9
  • 5
    This is an event handler for when the navigation already started. Would be better to not fire this if you want to prevent the NavigationStart. – Edgar Quintero Jul 15 '19 at 17:42