简体   繁体   中英

how to add CanDeactivate functionality in component?

I am trying to add CanDeactivate functionality in my component.I have a form in which there is one input field and button. i want if user enter something in input field and move to next screen without submit it will show a dialog box .if user enter yes from the dialog bix then it go to next component else it remain in same screen .

here is my code https://stackblitz.com/edit/angular-ctwnid?file=src%2Fapp%2Fhello.component.ts

import {CanDeactivate} from '@angular/router';
import { HelloComponent } from './hello.component';

export default class DeactivateGuard implements CanDeactivate<HelloComponent> {

  canDeactivate(component: HelloComponent): boolean {

    if (!component.canDeactivate()) {
      if (confirm('You have unsaved changes! If you leave, your changes will be lost.')) {
        return true;
      } else {
        return false;
      }
    }
    return true;
  }

}

currently when I am typing something on input field and click next button it give me error

ERROR
Error: Uncaught (in promise): TypeError: Cannot read property 'ngInjectableDef' of undefined
TypeError: Cannot read property 'ngInjectableDef' of undefined
at resolveNgModuleDep (https://angular-ctwnid.stackblitz.io/turbo_modules/@angular/core@6.0.0/bundles/core.umd.js:9309:31)
at NgModuleRef_.get (https://angular-ctwnid.stackblitz.io/turbo_modules/@angular/core@6.0.0/bundles/core.umd.js:10003:16)
at PreActivation.getToken (https://angular-ctwnid.stackblitz.io/turbo_modules/@angular/router@6.0.0/bundles/router.umd.js:3014:25)
at MergeMapSubscriber.eval [as project] (https://angular-ctwnid.stackblit

I think an ideal implementation would be to create an interface that would allow the Guard to be reusable.

Here's how:

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

export interface CanComponentDeactivate {
  confirm(): boolean;
}

@Injectable()
export class DeactivateGuard implements CanDeactivate < CanComponentDeactivate > {
  canDeactivate(
    component: CanComponentDeactivate,
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): boolean {
    if (!component.confirm()) {
      return confirm('You have unsaved changes! If you leave, your changes will be lost.');
    }
  }
}

Then this CanComponentDeactivate Interface should be implemented the component on which you have to place this guard. That's how it would be forced to implement the confirm method from where the returned boolean value is what you'd want to check in the canDeactivate method of your guard.

Something along the lines of this:

import { Component, Input } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { CanComponentDeactivate } from './deactivate.guard';

@Component({
  selector: 'hello',
  template: `<h1>Hello {{name}}!</h1>
      <form novalidate [formGroup]="sfrm" class="calform">
      <input type="text" formControlName="name"/>
      <button type="submit">submit</button>
      </form>
      <a [routerLink]="['/next']">next</a>
  `,
  styles: [`h1 { font-family: Lato; }`]
})
export class HelloComponent implements CanComponentDeactivate {
  @Input() name: string;
  sfrm: FormGroup

  constructor(private fb: FormBuilder) {
    this.sfrm = this.fb.group({
      name: ['']
    });
  }

  confirm() {
    return this.sfrm.submitted || !this.sfrm.dirty;
  }

}

One final thing would be to also add the Guard as a provider. After all, it's a service. So add it to the providers array:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { RouterModule, Routes } from '@angular/router';

import { ReactiveFormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { HelloComponent } from './hello.component';
import { ErrorComponent } from './error.component';
import { DeactivateGuard } from './deactivate.gaurd';

import { TestService } from './test.service';
import { TestResolver } from './test.resolver';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { NextComponent } from './next/next.component';
const routes: Routes = [{
    path: 'home',
    component: HelloComponent,
    canDeactivate: [DeactivateGuard]
  },
  {
    path: 'next',
    component: NextComponent
  },
  {
    path: '',
    redirectTo: '/home',
    pathMatch: 'full'
  }
];
@NgModule({
  imports: [
    BrowserModule,
    ReactiveFormsModule,
    RouterModule.forRoot(routes),
    HttpClientModule, 
    FormsModule
  ],
  declarations: [
    AppComponent,
    HelloComponent, 
    ErrorComponent, 
    NextComponent
  ],
  bootstrap: [AppComponent],
  providers: [
    TestService, 
    TestResolver, 
    DeactivateGuard
  ]
})
export class AppModule {}

This should make the guard work for you. Here's your Updated StackBlitz

you have not passed the deactivae guard in providers on app.module.ts , edited your stackbiltz

     providers: [TestService,TestResolver,DeactivateGuard  
  ]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM