Search code examples
angularroutes

Angular prevent URL navigation but keep button navigation


I'm currently trying to prevent the user from navigate through the url itself. The user should only be able to navigate with buttons.

Basically my routing looks like this :

    const routes: Routes = [
  { path: 'form', component: FormComponent},
  { path: '', redirectTo: '/form', pathMatch: 'full' },
  { path: 'attachment', component: AttachmentComponent },
  { path: 'inbox', component: InboxComponent},
  { path: 'summary', component: SummaryComponent },
  { path: 'answer/:id', component: SummaryComponent}
];  

So basically the user should be able to navigate to "/form" and "answer/:id" with the url itself but all the other routes like attachment/inbox/summary should be accessable by buttons only (e.g user goes on /form and theres a button which directs to attachment and summary or the user goes on "/answer:id" with a button with directs to inbox).

First thing I tried was setting the following parameter in my routing module(without setting a guard) :

@NgModule({
  imports: [RouterModule.forRoot(routes, { initialNavigation: false })],
  exports: [RouterModule]
})

Navigate through the url was not possible(which is good) and navigate with buttons worked but sadly I was not able to open my "form" or "answer" route which is necessary.
I tried setting up a guard :

const routes: Routes = [
  { path: 'form', component: FormComponent},
  { path: '', redirectTo: '/form', pathMatch: 'full' },
  { path: 'attachment', component: AttachmentComponent , canActivate:[DirectAccessGuard]},
  { path: 'inbox', component: InboxComponent, canActivate: [DirectAccessGuard]},
  { path: 'summary', component: SummaryComponent, canActivate: [DirectAccessGuard]},
  { path: 'answer/:id', component: SummaryComponent}
];

Guard :

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router} from '@angular/router';
import { Observable } from 'rxjs';
@Injectable({
  providedIn: 'root'
})
export class DirectAccessGuard implements CanActivate {
  constructor(private router: Router) { }
  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    // If the previous URL was blank, then the user is directly accessing this page
    return false;
  }
}

Opening "form" and "answer" with the url is possible but redirect to another component with a buttons doesn't work. Makes senses because I'm always returning false.
So my question is : Is it possible to get the target url in my guard?
If im on /form and I want to redirect to /attachment I know I can get "/form" with this.router.url but I want the target url ("/attachment" in this case). Or is there any other method to solve my problem?


Solution

  • Yes, It is possible to get the target url in guard from ActivatedRouteSnapshot. The routeConfig property in ActivatedRouteSnapshot has a path property which gives target url.

    I had tested as per the above mentioned scenarios, Now the routes look like below,

    const routes: Routes = [
        { path: 'form', component: FormComponent },
        { path: '', redirectTo: '/form', pathMatch: 'full' },
        { path: 'attachment', component: AttachmentComponent, canActivate: [DirectAccessGaurd] },
        { path: 'inbox', component: InboxComponent, canActivate: [DirectAccessGaurd] },
        { path: 'summary', component: SummaryComponent, canActivate: [DirectAccessGaurd] },
        { path: 'answer/:id', component: SummaryComponent, canActivate: [DirectAccessGaurd] },
        { path: '**', component: NotfoundComponent }
    ]
    

    So, the actual logic goes in Gaurd,

    import { Injectable } from '@angular/core';
    import { CanActivate, RouterStateSnapshot, ActivatedRouteSnapshot, Router } from '@angular/router';
    import { Observable } from 'rxjs';
    
    @Injectable({
        providedIn: 'root'
    })
    export class DirectAccessGaurd implements CanActivate {
    
        constructor(private router: Router) { }
        canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    
            if(this.router.url === '/form') {
                if(next.routeConfig.path === 'attachment' || next.routeConfig.path === 'inbox') {
                    return true;
                }
            }
            if(next.routeConfig.path === 'answer/:id') {
                return true; // To allow answer/:id route through url
            }
            if(this.router.url.startsWith('/answer/')) {
                if(next.routeConfig.path === 'summary') {
                    return true;
                }
            }
    
            this.router.navigate(['/notfound'])
            return false;
        }
    }
    

    The logic is simple, First I am checking for current url to be '/form', and then checking if target url is attachment or inbox, Then I am redirecting to those respectively.