A step-by-step guide on extracting information from a component and using it within a Guard

A challenge I am facing is how to display a warning prompt on the screen if the user tries to leave a page after making changes to a form. I have successfully created a Guard that logs a message to the console when the user attempts to navigate away from the page. Now, my next step is to show a pop-up dialog box to warn them and give them the option to either cancel the navigation or proceed.

Within the component hosting the form, I have a method to detect any changes made to the form. What remains now is to implement the functionality described above.

Below is the code for my Guard:

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

@Injectable({
  providedIn: 'root'
})
export class RouterGuardGuard implements CanDeactivate<unknown> {
  canDeactivate(
    component: unknown,
    currentRoute: ActivatedRouteSnapshot,
    currentState: RouterStateSnapshot,
    nextState?: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    console.log('TESTING');

    return true;
  }

}

I have integrated the guard for my component in my app-routing-module.ts like this:

  { path: 'users/edit/:id', canDeactivate: [RouterGuardGuard], component: UserEditorComponent },

Therefore, whenever changes are made to the form in the UserEditorComponent, I need a dialog box to appear before the navigation takes place. I already have the dialog box component set up, which is used across various sections of my application.

Thank you

Answer №1

If you want to confirm the state of your component's form within the canDeactivate method, one approach is to create an interface that, when implemented in your component class, will provide the user's decision to leave or stay in the component.

To do this, define an interface in your guard like this:

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

Instead of using an unknown type, utilize the newly created interface:

 export class RouterGuardGuard implements CanDeactivate<CanComponentDeactivate>{...}

Then, in the canDeactivate method, simply return the result of calling this method on the component:

  canDeactivate(
    component: CanComponentDeactivate,
    currentRoute: ActivatedRouteSnapshot,
    currentState: RouterStateSnapshot,
    nextState?: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    return component.canDeactivate();
  }

Next, within your component, implement the canComponentDeactivate interface you have created. In the implemented canDeactivate method, check if your form is touched or dirty and prompt the user accordingly, returning a promise<boolean> or boolean. For example:

canDeactivate() {
 if (this.myForm.dirty || this.myForm.touched) {
      return new Promise((resolve) => {
        this.modalService.create({
          content:
            "Your have unsaved changes. Are you sure you want to return?",
          onOk: () => {
            resolve(true);
          },
          onCancel: () => {
            resolve(false);
          },
        });
      });
 }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Angular allows for creating a single build that caters to the unique global style needs of every

Currently, I am working on a project for two different clients, each requiring a unique style.css (Global CSS). My goal is to create a single production build that can be served to both clients, who have different domains. I would like the global style t ...

Angular 2's Multi-select dropdown feature allows users to select multiple options

Recently, I encountered an issue with customizing CSS for angular2-multiselect-dropdown. I found the solution in this link: https://www.npmjs.com/package/angular2-multiselect-dropdown. I have included my code below. Any assistance in resolving this matter ...

Is there a way to verify if the object's ID within an array matches?

I am looking to compare the ID of an object with all IDs of the objects in an array. There is a button that allows me to add a dish to the orders array. If the dish does not already exist in the array, it gets added. However, if the dish already exists, I ...

Error in NextJS: The name 'NextApplicationPage' cannot be found

const { Component, pageProps}: { Component: NextApplicationPage; pageProps: any } = props After implementing the code above with 'Component' type set to NextApplicationPage, an error message pops up stating, The name 'NextApplicationPage&ap ...

Tips for setting up automatic saving of a reactive form in Angular 4

Is there a way to automatically save content in a reactive form once the form is validated, without the need to click a save button? ...

Exploring Angular 8: Connecting elements to an array filled with objects

My goal is to achieve the following: https://i.sstatic.net/TQeKN.png In my form, I have 2 fields: description and price. When the plus button is clicked, additional input fields (Input 2 and Price 2) are generated dynamically. I want to bind these field ...

Discrepancies in ESLint outcomes during React app development

As a newcomer to React development, I am encountering discrepancies between the errors and warnings identified in my project's development environment versus its production environment. Strangely, I have not configured any differences between these en ...

Exploring the Yahoo Finance Websocket with Angular

Upon visiting the Yahoo finance website (for example, ), I noticed that the page establishes a WebSocket connection. The URL for this WebSocket is wss://streamer.finance.yahoo.com/ I'm currently working on a small project where I need to retrieve dat ...

What is the best way to create a dynamic icon using React and TypeScript?

Excuse me, I am new to using React and TypeScript. I'm having trouble getting the icon to change based on the status in AppointmentType. The body color is working fine, but the icons are not changing. Can someone please help me solve this issue? const ...

What is the best way to globally incorporate tether or any other feature in my Meteor 1.3 TypeScript project?

I've been working hard to get my ng2-prototype up and running in a meteor1.3 environment. Previously, I was using webpack to build the prototype and utilized a provide plugin to make jQuery and Tether available during module building: plugins: [ ne ...

The expansion feature of PrimeNG Turbotable

I'm currently facing an issue with the Primeng Turbotable where I am unable to expand all rows by default. You can find a code example of my problem at this link. I have already tried implementing the solution provided in this example, but unfortuna ...

How can I resolve a promise that is still pending within the "then" block?

Here is a piece of code that I have written: fetch(`${URL}${PATH}`) .then(res => { const d = res.json(); console.log("The data is: ", d); return d; }) When the code runs, it outputs The data is: Promise { <pending> ...

Angular 6 Observer - Double Authentication Triggered

I am currently learning about Observer and Http requests. My code is functioning properly, but it lacks elegance and the issue I am facing is that the URL is being called twice. isAuthenticated() { let obs = this.http.get('http://localhost:8080/a ...

Guide to Dynamically Including an Element in an Array using Typescript

Encountering a type error within the <RenderFormFields formFields={formFieldsData} /> component:- Types of property 'type' are not compatible. Type 'string' cannot be assigned to type '"select"'.ts(2322) Rende ...

Utilizing ngFor in Angular to dynamically refer to named variables within ngIf

I am looking to access the input element within the ngIf directive in order to check if it currently contains a specific value. The issue lies with the code inside the ngIf statement. <span *ngFor="let item of items;let i=index"> <input t ...

Typescript: Delay code execution until a function has completed running

I've encountered an issue with my code that involves calling a function. Here is the snippet of code in question: this.getAllOptions(questionID); console.log("++++++++++++++++++++++++++++++++"); console.log(this.result); The task of this function is ...

Tips on assigning array union as the return type of a function

I am working with a function parameter that accepts an array union, like this: (ClassA|ClassB)[]. How can I return either ClassA[] or ClassB[] from the function? When attempting to return type (ClassA|ClassB)[], I encounter the following error: Assig ...

Struggling to retrieve the header in Angular 6

When setting headers from an Express server written in NodeJS, I use the following code: app.use('/routes', function(req, res, next) { res.setHeader('test', 'test'); next(); ); The headers are successfully sent to th ...

"Error encountered: Route class unable to reach local function in TypeScript Express application" #codingissues

Experiencing an 'undefined' error for the 'loglogMePleasePlease' function in the code snippet below. Requesting assistance to resolve this issue. TypeError: Cannot read property 'logMePleasePlease' of undefined This error ...

What is the TypeScript syntax for indicating multiple generic types for a variable?

Currently working on transitioning one of my projects from JavaScript to TypeScript, however I've hit a roadblock when it comes to type annotation. I have an interface called Serializer and a class that merges these interfaces as shown below: interfa ...