4

I have a problem with handle back button in browser. I have a custom modal(just html/css, no bootstrap), and I want to show this modal when user click back button. If user choose Yes, browser will directly to previous page. When user choose No, the modal will disappear and not directly to another. I tried using candeactive and PlatformLocation, but it seem not work. I can't stop browser directly to previous page when click. Have any solution for this? If yes, please give me an example. Thanks all for help!

This is my code:

article.component.html

<div class="editor-page">
  <div class="container">
    <div class="row">
      <div class="col-md-8 offset-md-2">
        <form [formGroup]="articleForm" (ngSubmit)="handleSubmit()">
          <div class="form-group">
            <input type="text" name="title" class="form-control" placeholder="Article Title" formControlName="title">
          </div>
          <div class="form-group">
            <input type="text" name="body" class="form-control" placeholder="What's this article about?" formControlName="body">
          </div>
          <div class="form-group">
            <textarea name="description" class="form-control" id="" cols="30" rows="10" placeholder="Write your article (in markdown)" formControlName="description"></textarea>
          </div>
          <div class="form-group">
            <input type="text" name="tagList" class="form-control" placeholder="Enter tags" formControlName="tagList">
          </div>
          <button class="btn btn-md btn-success float-right">Publish Article</button>
        </form>
      </div>
    </div>
  </div>
</div>
<div class="modal" *ngIf="isBackClicked">
  <p>Do you want to quite?</p>
  <a (click)="handleYesBtn()">Yes</a>
  <a (click)="handleNoBtn()">No</a>
</div>

article.component.ts

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder } from '@angular/forms';
import { ArticleService } from '../../services/article.service';
import { ArticlePost } from '../../models/article';
import { Location } from "@angular/common";
import { Subscription } from 'rxjs/Subscription';
import { PlatformLocation } from '@angular/common'
import { Router } from '@angular/router';
@Component({
  selector: 'app-article',
  templateUrl: './article.component.html',
  styleUrls: ['./article.component.scss']
})
export class ArticleComponent implements OnInit {
  isBackClicked = false;
  articleForm: FormGroup;
  subscription: Subscription;
  constructor(
    private formBuilder: FormBuilder,
    private articleService: ArticleService,
    private router: Router,
    private location: PlatformLocation
  ) {
    this.articleForm = this.formBuilder.group({
      title: "",
      body: "",
      description: "",
      tagList: null
    })
    location.onPopState(() => {
      this.isBackClicked = true;
    });
  }

  ngOnInit() {
  }

  handleYesBtn() {
    this.router.navigateByUrl('/');
  }

  handleNoBtn() {
    this.isBackClicked = false;
  }

  handleSubmit() {
    const article = new ArticlePost(this.articleForm.value);

    this.articleService.addArticle(article).subscribe(data => {
      console.log('data', data);
    })
  }

}

When I clicked back button in browser, it's not show my modal and directly to previous page.

John
  • 207
  • 2
  • 5
  • 15
  • Possible duplicate of [How do I detect user navigating back in Angular2?](https://stackoverflow.com/questions/40381814/how-do-i-detect-user-navigating-back-in-angular2) – ibenjelloun Jul 16 '18 at 13:36
  • Hi @ibenjelloun, i tried follow that post but I can't stop it directly to previous page. If possible, can you help me? – John Jul 16 '18 at 13:39
  • Please specify in your question what you already tried with some code and what is the issue you are facing. It's appreciated to share a minimal reproduction code on stackblitz or other online editors. – ibenjelloun Jul 16 '18 at 13:43
  • Yes, I will edited to show my code. Please give me some time. Thanks! – John Jul 16 '18 at 13:46
  • Hi @ibenjelloun, I editted my post, sir – John Jul 16 '18 at 14:08
  • Did you see this post : https://stackoverflow.com/questions/45865929/angular-how-do-i-cancel-a-route-change-triggered-from-the-back-button ? – ibenjelloun Jul 16 '18 at 14:16
  • Yes, I tried this, I using Location, but it's seem not work too. I tried CanDeactive., i can show my modal, but when click No, the modal hidden but when click back button again, nothing is appear. I following this post: https://www.squeed.com/2017/12/18/how-to-use-a-custom-dialog-with-the-candeactivate-route-guard-in-angular/ – John Jul 16 '18 at 14:33
  • I have debug it. When click back button in first time, I have run to candeactive function in service, but if choose No and click again, It's not run candeactive function. – John Jul 16 '18 at 14:35

2 Answers2

7

According to the angular.io, you can create a CanDeactivate guard for this kind of uses.

I started from a simple example having two routes with two components HelloComponent and ByeComponent. To leave the HelloComponent, a confirmation dialog is displayed.

First I copied the CanDeactivateGuard from the angular.io example :

import { Injectable }    from '@angular/core';
import { CanDeactivate } from '@angular/router';
import { Observable }    from 'rxjs';

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

@Injectable({
  providedIn: 'root'
})
export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> {
  canDeactivate(component: CanComponentDeactivate) {
    return component.canDeactivate ? component.canDeactivate() : true;
  }
}

Then I added the CanDeactivate to the routing of component that need confirmation :

@NgModule({
  imports: [
    RouterModule.forChild([
      { path: '', component: HelloComponent,  canDeactivate: [CanDeactivateGuard] }
    ])
  ],
  exports: [RouterModule]
})
export class HelloRoutingModule { }

Finally implemented the canDeactivate function in the related component :

canDeactivate() {
    return confirm('Are you sure you want to leave Hello ?');
}

Here is the link for the running example.

ibenjelloun
  • 7,425
  • 2
  • 29
  • 53
  • Hi @ibenjelloun, it's work for me. Thanks. But can we just show confirm modal when clicked back?. When click submit, I don't want to show this dialog. This is code: https://stackblitz.com/edit/stackoverflow-51363122-x48d4t – John Jul 16 '18 at 14:46
  • I editted in stackblitz, please help me check it. Thanks! – John Jul 16 '18 at 14:51
  • 1
    `canDeactivate` takes promise or boolean as a return value. Here is an edit : https://stackblitz.com/edit/stackoverflow-51363122-2 – ibenjelloun Jul 16 '18 at 14:54
  • Thanks for your explain. So Can I change confirm dialog by my custom modal? Because with the modal, I will show it with multiple language. – John Jul 16 '18 at 14:59
  • 1
    Yes for sure, you just need to return a promise, you can even use the camera and ask for a smile to confirm :). – ibenjelloun Jul 16 '18 at 15:01
  • Yes, I will try. Thank you so much :) – John Jul 16 '18 at 15:14
5

You can use HostListener to handle back button of the browser

 import { HostListener } from '@angular/core';

@HostListener('window:popstate', ['$event'])
  onPopState(event) {
    console.log('Back button pressed');
    //Here you can handle your modal
  }
ishivamjain
  • 121
  • 1
  • 6