Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add canDeactivateChild guard #11836

Open
marcelgood opened this issue Sep 22, 2016 · 27 comments
Open

Add canDeactivateChild guard #11836

marcelgood opened this issue Sep 22, 2016 · 27 comments
Labels
area: router feature: under consideration Feature request for which voting has completed and the request is now under consideration feature Issue that requests a new feature freq2: medium
Milestone

Comments

@marcelgood
Copy link

marcelgood commented Sep 22, 2016

I'm submitting a ... (check one with "x")

[ ] bug report => search github for a similar issue or PR before submitting
[x] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

What is the motivation / use case for changing the behavior?
There is already a canActivateChild guard. Having the symmetric guard is equally important. Not sure why this wasn't added at the same time. We have many scenarios where we need to protect child routes from navigating away if they have pending unsaved changes. That's currently only possible by putting a canDeactivate guard on each child route.

  • Angular version: Latest
  • Browser: all
  • Language: all
@vicb vicb added feature Issue that requests a new feature area: router labels Sep 22, 2016
@nlgi
Copy link

nlgi commented Sep 26, 2016

+1, as canActivateChild, we have to deactivate child route.

Is that possible without a new "canDeactivateChild guard"?

Many thanks,

@popinguy
Copy link

popinguy commented Dec 2, 2016

Are there any news for this issue? Or are there any other solutions as @nlgi asked?
Thanks

@vitorrd
Copy link

vitorrd commented Mar 6, 2017

Anything on this? I really wouldn't like to edit Ng2 from the inside, but that's what I've done so far.

@ghost
Copy link

ghost commented Mar 6, 2017

@vitorrd have you make a turn around about this issue by editing Ng2 from the inside? I tried to follow the steps provided by tdsmithATabc from #11664 (it's at the end of the page) but I can't make it work yet.

@vitorrd
Copy link

vitorrd commented Mar 6, 2017

@fenderjair It seems the fix provided is fairly useful in some cases, just not in mine. What I want is to avoid any navigation while a custom popup is open, so I need to cover a big amount of routes.

@patrickpereira
Copy link

+1 Needed for lazyloaded routes

@hydralix
Copy link

hydralix commented Jul 7, 2017

+1

@elvisbegovic
Copy link
Contributor

elvisbegovic commented Jul 13, 2017

any news here ? this feature request is missing to fix my usecase.

actually I have to specify canDeactivate:[CanDeactivateEntityGuard] in all child. With feature like canDeactivateChild I coul'd specify it only once in routes[] array used in forRoot()

canDeactivateChild need call my guard each time when child route or sub-child or sub-sub-child route change.

Actually, is there better way to avoid write it in every routes child? I mean handle all route deactivation and call my guard?
merci
cc @vicb

@erc-s-arai
Copy link

are there any news or solution here?
I would need canDeactivateChild at least in next year.

Please tell me about when will that be fixed?

@jtkDvlp
Copy link

jtkDvlp commented Oct 5, 2017

are there any news, solution or workarounds here?

@elvisbegovic
Copy link
Contributor

+2

@spoddutu
Copy link

spoddutu commented Nov 21, 2017

+1. As of now any news on this issue?

And BTW below is path i took to implement this as of now. Please suggest in case of any better Path

Listening to valueChanges event of the NgForm and emitting an event from child component and subscribing it in parent to return proper value when Deactivate guard is fired.

@elldawg
Copy link

elldawg commented Jan 8, 2018

Why all of the downvotes without any reason as to why this functionality isn't needed? I actually have the exact same situation. I believe that I can use the canActivateChild at the root. This should prevent any route changes, right?

@cjc343
Copy link
Contributor

cjc343 commented Jan 8, 2018

The downvotes seem to be a response to '+1' comments which add nothing to the discussion vs adding a reaction to the original proposal, rather than a response to the proposal itself.

@ngbot ngbot bot added this to the Backlog milestone Jan 23, 2018
@salilbajaj
Copy link

Any solution ?

@mihaivarga
Copy link

Any workaround?

@shlomiassaf
Copy link
Contributor

Bump...

This is really something that we need in order to create proper abstractions for large apps...

In large app's having a guard definition per route doesn't scale...

Having canActivateChild already implemented should make most of the work copy paste... can this get priority?

Thanks!

@shlomiassaf
Copy link
Contributor

Here is a detailed explanation why it is needed and how much it will help reducing code and boilerplate in large code base projects.

https://medium.com/@shlomiassaf/blocking-page-navigation-in-angular-with-scale-6758aee4b821?source=safariShare-a0aebecf9775-1541710717

@msqaddura
Copy link

msqaddura commented Jan 14, 2019

As per the workaround

  @ Component(...)
  class Parent {
  @ ViewChild(Child) child;
  canDeactivate(){
     return this.child.canDeactivate();
  }
 }

  @ Component(...)
  class Child {
     canDeactivate(){
         return confirm("you sure?")
     }
  }

Should do the trick.

But as per the feature request, Definitely a PLUS +1.
as it gets tiring to keep doing the same copy paste over & over!

Note here I did not use@HostListener('window:beforeunload', ['$event']) since if it is used to compensate the canDeactivateChild then it can as well compensate the canDeactivate. leaving canDeactivate unused

@jpsfs
Copy link

jpsfs commented Feb 5, 2019

Also facing this problem right now.
Implementing @msqaddura's workaround is very tricky on large applications. It's virtually impossible to run down several hundred modules to put a canDeactivate guard on each of them.

Any idea on the timeline for this?

@MoxxiManagarm
Copy link

I now also face the same issue. The workaround is not acceptable in my case. Is there anything new?

@nullifiedtsk
Copy link

nullifiedtsk commented Sep 13, 2019

+1, but should tell, that i got some success with CanActivateChild in my project.
I need to block router navigation when any dialog is opened. Package ngx-modialog is used to show modals.

import { ActivatedRouteSnapshot, CanActivateChild, RouterStateSnapshot, UrlTree, Router } from '@angular/router';
import { Modal } from 'ngx-modialog';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Location } from '@angular/common';

@Injectable()
export class DialogOpenedGuard implements CanActivateChild {
    constructor(private modal: Modal, private router: Router, private location: Location) {
    }

    private hasSomeModalsOpened() { ... /* modal hook on create with stores or something like that */ }

    private pushHistory(state: RouterSnapshot) {
            // something like
            this.location.go(state.url);
    }

    canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
        if (this.hasSomeModalsOpened()) {
            this.pushHistoryBack(state);
            return false;
        }

        return true;
    }
}

But i also thinks that calling canDeactivateChild will be more clear than checking if you can activate new route. It's just not clear to understand what will happen if you will prevent navigation by implementing CanActivateChild guard.

@nullifiedtsk
Copy link

nullifiedtsk commented Mar 3, 2020

There is another way to block pending navigations - You can override Location and write your own implementations of Location.subscribe method. If you never call onNext arg, navigation will never happen. Only thing you should do after is to push prevously visited page back to history. You can also inject simple services there.

But i don't think it's matching answer to initial question... and not sure it's legal use of Location :|

Anyway, successfully used this to block navigation events when some overlays presents on page.

@philip-firstorder
Copy link

I also lost several hours because of this. Any news yet?

@msqaddura
Copy link

msqaddura commented Apr 28, 2020

this seems promising, not the full solution but covers some
https://github.com/ngneat/dirty-check-forms

@rickwalking
Copy link

rickwalking commented May 19, 2020

There are workarounds to this, but this is really a needed feature.

@funky-jojo
Copy link

funky-jojo commented Jul 30, 2020

I have been using the following workaround for a while with no problems that I'm aware of...

The basic idea is to perform a pre-check every time a route is navigated, and manually intervene to ensure that the desired guards are in place.

The example below will ensure that MySpecialCanDeactivateGuard is enforced for all routes in the application, including child routes and dynamically added routes.

set up the logic in the root app.component constructor

export class AppModule {

  constructor(globalRouteGuardService: GlobalRouteGuardService) {
    globalRouteGuardService.ensureGlobalGuards();
  }

 ...

global-route-guard.service.ts

import { Injectable } from "@angular/core";
import { Router, GuardsCheckStart, Route } from "@angular/router";
import { MySpecialCanDeactivateGuard} from "./my-special-can-deactivate-guard";


@Injectable({ providedIn: "root" })
export class GlobalRouteGuardService {

    constructor(private router: Router) { }

    /** This will help ensure that routes are automatically protected by ensuring that global guards are applied to every route */
    public ensureGlobalGuards() {
        this.router.events.subscribe((e: GuardsCheckStart) => {
            if (e instanceof GuardsCheckStart) {
                let lastChild: ActivatedRouteSnapshot = e.state.root;
                while (lastChild && lastChild.firstChild) {
                    lastChild = lastChild.firstChild;
                }

                if (lastChild && lastChild.routeConfig) {
                    this.ensureCanDeactivateGuards(lastChild.routeConfig);
                }
            }
        });
    }

    private ensureCanDeactivateGuards(routeConfig: Route) {
        if (!routeConfig.canDeactivate) {
            routeConfig.canDeactivate = [];
        }

        const canDeactivateGuards = routeConfig.canDeactivate;

// ensure that the "MySpecialCanDeactivateGuard" is in effect.
        if (!canDeactivateGuards.find(o => o == MySpecialCanDeactivateGuard)) {
            canDeactivateGuards.push(MySpecialCanDeactivateGuard);
        }
    }
}

@angular-robot angular-robot bot added the feature: under consideration Feature request for which voting has completed and the request is now under consideration label Jun 4, 2021
@petebacondarwin petebacondarwin added this to Yes but decide effort in Feature Requests Jul 2, 2021
@alxhub alxhub moved this from Yes but decide effort to Backlog in Feature Requests Jul 8, 2021
aahmedayed added a commit to aahmedayed/angular that referenced this issue Nov 10, 2021
this PR is to add `canDeactivateChild` to the route

resolves angular#11836
aahmedayed added a commit to aahmedayed/angular that referenced this issue Nov 10, 2021
this PR is to add `canDeactivateChild` to the route

resolves angular#11836
aahmedayed added a commit to aahmedayed/angular that referenced this issue Nov 10, 2021
this PR is to add `canDeactivateChild` to determine if the current user is allowed to deactivate a child of the root.

resolves angular#11836
aahmedayed added a commit to aahmedayed/angular that referenced this issue Nov 10, 2021
this PR is to add `canDeactivateChild` to determine if the current user is allowed to deactivate a child of the root.

resolves angular#11836
aahmedayed added a commit to aahmedayed/angular that referenced this issue Nov 10, 2021
this PR is to add `canDeactivateChild` to determine if the current user is allowed to deactivate a child of the root.

resolves angular#11836
aahmedayed added a commit to aahmedayed/angular that referenced this issue Nov 10, 2021
this PR is to add `canDeactivateChild` to determine if the current user is allowed to deactivate a child of the root.

resolves angular#11836
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: router feature: under consideration Feature request for which voting has completed and the request is now under consideration feature Issue that requests a new feature freq2: medium
Projects
No open projects
Development

Successfully merging a pull request may close this issue.