Reputation: 12729
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/[email protected]/bundles/core.umd.js:9309:31)
at NgModuleRef_.get (https://angular-ctwnid.stackblitz.io/turbo_modules/@angular/[email protected]/bundles/core.umd.js:10003:16)
at PreActivation.getToken (https://angular-ctwnid.stackblitz.io/turbo_modules/@angular/[email protected]/bundles/router.umd.js:3014:25)
at MergeMapSubscriber.eval [as project] (https://angular-ctwnid.stackblit
Upvotes: 1
Views: 6544
Reputation: 39472
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
Upvotes: 5
Reputation: 1499
you have not passed the deactivae guard in providers on app.module.ts , edited your stackbiltz
providers: [TestService,TestResolver,DeactivateGuard
]
Upvotes: 0