ng-router-freeze

A better, simpler yet more powerful way to prevent navigation in Angular apps.

Usage no npm install needed!

<script type="module">
  import ngRouterFreeze from 'https://cdn.skypack.dev/ng-router-freeze';
</script>

README

Angular Router Freeze

A better, simpler yet more powerful way to prevent navigation in Angular apps.

npm i ng-router-freeze

Features

In contrast with Angular's built-in CanDeactivate based setup

  • ✅ Super minimal setup, to get navigation blocking just decorate > allow > enable, and you're set!
  • ✅ Super DRY. Perhaps TOOOO DRY.
  • ✅ Allows even the child components to "deactivate" a route as opposed to just the component set in route config.
  • ✅ Integration tested with Angular 8 (6, 7, 9 coming soon)
  • 🚧 TODO - Deals with the infamous routing history bug known since 2016!!

Usage

As easy as 1, 2, 3!

1️⃣ First, include the NgRouterFreezeModule module in the AppModule:

  import { BrowserModule } from '@angular/platform-browser';
+ import { NgRouterFreezeModule } from 'ng-router-freeze';
  import { AppRoutingModule } from './app-routing.module';

  @NgModule({
    imports: [
      BrowserModule,
+     NgRouterFreezeModule,
      AppRoutingModule
    ]
  })
  export class AppModule { }

2️⃣ Then, add the navigation blocking logic to components:

  // edit-report.component.ts

  import { Component,  OnInit } from '@angular/core';
+ import { WithNavigationBlocking, NavigationBlocking } from 'ng-router-freeze';
  import { ModalService } from '../services/modal.service';

+ @WithNavigationBlocking()
  @Component({
    selector: 'dash-edit-report',
    templateUrl: `./edit-report.component.html`,
    styleUrls: ['./edit-report.component.scss']
  })
- export class EditReportComponent implements OnInit {
+ export class EditReportComponent implements OnInit, NavigationBlocking {

    constructor(private modalService: ModalService) {}

    ...

+   canNavigate() {
+     return this.isDraftSaved() || this.modalService.confirm('Discard changes?');
+   }

    ...
  }

3️⃣ Lastly, allow the component(s) to block navigation:

Navigation blocking will not kick in until the component is explicitly allowed for some or all routes in route config

  import { NgModule } from '@angular/core';
  import { RouterModule, Routes } from '@angular/router';
+ import { NavigationGuard, enableNavigationBlocking } from 'ng-router-freeze';

  import { ReportService } from './services/report.service';
  import { ViewReportComponent } from './components/view-report.component';
  import { NewReportComponent } from './components/new-report.component';
  import { EditReportComponent } from './components/edit-report.component';
  import { ReviewReportComponent } from './components/review-report.component';

  export const routes: Routes = [
    {
      path: ':id',
      data: { title: 'Report' },
      component: ViewReportComponent,
+     canDeactivate: [NavigationGuard]
    },
    {
      path: 'new',
      data: { title: 'New Report' },
      component: NewReportComponent,
+     canDeactivate: [NavigationGuard]
    },
  ];

+ enableNavigationBlocking(routes, [
+   { path: ':id', allow: [EditReportComponent, ReviewReportComponent] },
+   { path: 'new', allow: [EditReportComponent] }
+ ]);

  @NgModule({
    declarations: [
      ReportSummaryComponent,
      NewReportComponent,
      EditReportComponent,
      ReviewReportComponent
    ],
+   // works with child routes as well
    imports: [RouterModule.forRoot(routes)],
    providers: [ReportService]
  })
  export class AppRoutingModule { }

👏👏 And that's it!

License

Licensed under MIT