7

I have this interface that i'm using to prevent the user to leave page

export interface ComponentCanDeactivate {
  canDeactivate: () => boolean;
}

@Injectable()
export class PendingChangesGuard implements CanDeactivate<ComponentCanDeactivate> {
  canDeactivate(component: ComponentCanDeactivate): boolean {
    return  component.canDeactivate() ?
     //code : //more code
  }
}

In one of my component i have the following code

export class DashboardComponent implements ComponentCanDeactivate{
  @HostListener('window:beforeunload')
  canDeactivate(): boolean {
    return !this.isDirty;
  }

My problem is that my component -> (component: ComponentCanDeactivate) from PendingChangesGuard is always null so i get an error saying

Cannot call canDeactivate() of null

I also have this setup in my routing

 path: 'dashboard',
        canDeactivate: [PendingChangesGuard],
        loadChildren: './views/dashboard/dashboard.module#DashboardModule'

Can someone tell me what am i doing wrong?

Varun Sukheja
  • 6,170
  • 5
  • 51
  • 93
user9052661
  • 161
  • 1
  • 5
  • @MilanRaval Yes, i implemented ComponentCanDeactivate and rewrite the function canDeactive(): boolean. (You can see the code in the middle of my post). It only returns if form is dirty or not – user9052661 Dec 04 '17 at 20:08

3 Answers3

9

The issue was caused by lazy loading

Instead of having this in your app routing:

path: 'dashboard',
        canDeactivate: [PendingChangesGuard], <-- causing issue
        loadChildren: './views/dashboard/dashboard.module#DashboardModule'

You need to remove the canDeactive from the app routing and move it to the module routing.

const routes: Routes = [
  {
    path: '',
    component: DashboardComponent,
    canDeactivate: [ PendingChangesGuard ]
  }
user9052661
  • 161
  • 1
  • 5
0

In your PendingChangesGuard, try to inject the component itself, not the interface:

export class PendingChangesGuard implements CanDeactivate<DashboardComponent> {
  constructor() {}
  canDeactivate(component: DashboardComponent): boolean {
  ...
  }

You cannot inject an interface using Angular DI as interfaces are just Typescript constructs and do not exist in Javascript code produced with the compilation process.

For more information, have a look at this SO question.

Fjut
  • 1,314
  • 12
  • 23
  • Thanks for replying, I tried it and i got the same error which is: TypeError: Cannot read property 'canDeactivate' of null – user9052661 Dec 04 '17 at 20:49
  • I think that you need to declare a component in your routing setup for guards to work. Something like: path: 'dashboard', canDeactivate: [PendingChangesGuard], component: DashboardComponent What you could do is setup your guard 'lower' in the dashboard module routing configuration i suppose. Let me know if it works. – Fjut Dec 05 '17 at 08:09
  • This is what i have in my routing: path: 'dashboard', canDeactivate: [PendingChangesGuard], loadChildren: './views/dashboard/dashboard.module#DashboardModule' } – user9052661 Dec 05 '17 at 14:35
  • Yes, i see that from the question. From my understanding, you cannot add a route guard that depend on some component logic without specifying a component in the route configuration and that is why you always get an exception. – Fjut Dec 05 '17 at 14:46
0

I implement like this

Deactive-guard-service.ts

export interface CanComponentDeactivate {
  canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean;
}

@Injectable()
export class DeactivateGuardService implements  CanDeactivate<CanComponentDeactivate>{

  canDeactivate(component: CanComponentDeactivate) {
    return component.canDeactivate ? component.canDeactivate() : true;
  }
}

Component.ts

checkSave(): Promise<boolean> {
    var prom = new Promise<boolean>((resolve, reject) => {
      //check saved change
        if(saved) resolve(true);
        else reject(false);
    });
    return prom;
  }

  canDeactivate(): Promise<boolean> {

    return this.checkSave().catch(function () {
      return false;
    });
  }
Dakota
  • 495
  • 2
  • 10
  • I tried implementing like you did, it gave me the same error. It tells me that : TypeError: Cannot read property 'canDeactivate' of null – user9052661 Dec 05 '17 at 14:36
  • I think your issue cause u are using lazy load module . You can refer to this topic https://github.com/angular/angular/issues/16868 Try put CanDeactive into main route of DashboardModule – Dakota Dec 06 '17 at 02:29
  • Thanks @Dakota that was the issue! – user9052661 Dec 06 '17 at 17:51