0

I created an application where the user inputs data. On that application, I want to implement router guards to deny the user to go back on-page so that they don't lose their data. If the user clicks on the back button on the browser, it reloads page instead of going back?

I am thinking to use canDeactivate to deny access to the previous page and Angular Location to determine what page users are on then reload that page. But I don't how to implement this.

Shank
  • 413
  • 4
  • 15
  • 1
    https://medium.com/better-programming/angular-how-keep-user-from-lost-his-data-by-accidentally-leaving-the-page-before-submit-4eeb74420f0d – MS_AU Oct 31 '19 at 22:00
  • Why don't you store the datas somewhere instead? – Sa Hagin Oct 31 '19 at 22:00
  • What have you tried so far? The article that @MS_AU provided a link to looks like a pretty solid starting point! – Narm Oct 31 '19 at 22:26
  • @Narm I actually looked into this article https://medium.com/engineering-on-the-incline/reloading-current-route-on-click-angular-5-1a1bfc740ab2 – Shank Nov 01 '19 at 11:19

1 Answers1

5

1. Create Service for CanDeactivate Guard

First you have to create an interface that will declare canDeactivate method and using this interface you will create a service that will act as canDeactivate guard. This service will define canDeactivate method as following:

deactivate.guard.ts:

import { Injectable } from '@angular/core';
import { CanDeactivate } from '@angular/router';

export interface CanComponentDeactivate {
  canDeactivate(): boolean;
}

@Injectable()
export class DeactivateGuard implements CanDeactivate<CanComponentDeactivate> {
  canDeactivate(component: CanComponentDeactivate): boolean {

    /*
    The return value would be true, unless the canActivate function, 
    defined on the component returns false,
    in which case the function will open a Dialog Box,
    to ask if we want to stay on the page or leave the page.
    */
    if (component.canDeactivate()) return true;
    else return confirm('You have unsaved changes!') ? true : false;

  }
}

The interface has declared canDeactivate method whose return type is boolean. In the service code, we called canDeactivate method using component instance.

2. Configure CanDeactivate Guard Service in Application Routing Module

app.module.ts:

import { CanDeactivateGuard } from './can-deactivate-guard.service';

------
@NgModule({
  ------
  providers: [ 
    CanDeactivateGuard
  ]
})
export class AppRoutingModule { } 

3. Create canDeactivate() method within Your Component

formComponent.ts:

import { Component, HostListener } from '@angular/core';
import { FormGroup, FormBuilder } from '@angular/forms';
import { CanComponentDeactivate } from 'src/app/deactivate.guard';

@Component({
  selector: 'app-form',
  templateUrl: './form.component.html',
  styleUrls: ['./form.component.scss']
})
export class FormComponent implements CanComponentDeactivate {
  saved: boolean;
  form: FormGroup;
  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      name: ['']
    });
  }

  /* Prevent page reloading */
  @HostListener('window:beforeunload', ['$event'])
  canReload(e) {
    if (!this.canDeactivate()) e.returnValue = true;
  }

  submit = () => this.saved = true;
  canDeactivate = () => this.saved || !this.form.dirty;
}

4. Add CanDeactivate Guard to Component Route in Routing Module

You need to add our CanDeactivate guard .i.e. DeactivateGuard to component route in routing module using canDeactivate attribute.

app-routing.module.ts:

const routes: Routes = [
  {
    path: 'home',
    component: FormComponent,
    canDeactivate: [DeactivateGuard]
  },
  { path: 'next', component: NextComponent },

  { path: '', redirectTo: '/home', pathMatch: 'full' }
];

You might as well consider storing your data in a service as it might be a better choice.

Rafi Henig
  • 5,950
  • 2
  • 16
  • 36
  • You're totally right let me see what I can do about it – Rafi Henig Nov 01 '19 at 10:47
  • I've updated the answer, let me know if it works for you. – Rafi Henig Nov 01 '19 at 10:55
  • can I do this for multiple pages? – Shank Nov 01 '19 at 11:17
  • let me understand what you want. as far as understood you want your page to refresh on back navigation, How do you expect the data to be saved? a better approach is to prevent reloading or any page navigation when there's unsaved data. – Rafi Henig Nov 01 '19 at 11:20
  • Thanks I was actually trying different options but I think it would be best to prevent any page navigation – Shank Nov 01 '19 at 11:23
  • if (this.canDeactivate()) return; this doesn't make sense? – Shank Nov 01 '19 at 11:49
  • Unless you return false the the page will be reloaded – Rafi Henig Nov 01 '19 at 11:50
  • I've tried this and window alert comes up when i go next page but doesnt give any alert when i go back – Shank Nov 01 '19 at 12:25
  • could you post your DeactiveGuard? it worked for me. – Rafi Henig Nov 01 '19 at 12:27
  • ```export class DeactivateGuard implements CanDeactivate { canDeactivate(component: CanComponentDeactivate): boolean { if (component.canDeactivate()) return true; else return confirm('You have unsaved changes!') ? true : false; } } ``` – Shank Nov 01 '19 at 12:29