123

Is there any not-so-complicated way to make a confirm dialog in angular 2, the idea is to click on an item and then show a popup or modal to confirm its deletion, I tried angular 2 modals from here angular2-modal, but I don't know how to make that if you confirm or cancel it does something. the click function works fine, the only problem is that I don't know too well how to use it. I also have another modal with the same plugin with the difference that I use.

this.modal.open(MyComponent);

And I don't want to create another component just for show a confirmation box that's why I'm asking.

Liam
  • 27,717
  • 28
  • 128
  • 190
victor dencowski
  • 1,398
  • 2
  • 12
  • 12
  • 1
    "The easy way" would be to use something like angular2-modal like you mentioned. Otherwise you'll have to build yourself one, and that I might not call as an easy way. Did you see on the link you provided that there was plunker examples showing the usage :) http://embed.plnkr.co/mbPzd8/ – AT82 Jan 16 '17 at 20:36
  • i see, then it will be a good idea to create a component with the confirm popup that has 2 buttons and when clicked execute a function in the main component. In the example he uses the keyup 5 to close the dialog i need to close it and call a function. Anyways, will try to make as simple and clear as i can and post results – victor dencowski Jan 16 '17 at 20:51
  • There are other modals out there as well, that might have other built-in features that you might wish for, but your requirements don't seem too difficult, so I guess many would be suitable for you, but if your like, just browse around and find one that you like. Here is some examples: https://www.npmjs.com/search?q=angular+2+modal Try them out and come back to SO if you encounter problems! Happy coding! :) – AT82 Jan 16 '17 at 20:54

9 Answers9

266

Method 1

One simple way to confirm is to use the native browser confirm alert. The template can have a button or link.

<button type=button class="btn btn-primary"  (click)="clickMethod('name')">Delete me</button>

And the component method can be something like below.

clickMethod(name: string) {
  if(confirm("Are you sure to delete "+name)) {
    console.log("Implement delete functionality here");
  }
}

Method 2

Another way to get a simple confirmation dialog is to use the angular bootstrap components like ng-bootstrap or ngx-bootstrap. You can simply install the component and use the modal component.

  1. Examples of modals using ng-bootstrap
  2. Examples of modals using ngx-bootstrap.

Method 3

Provided below is another way to implement a simple confirmation popup using angular2/material that I implemented in my project.

app.module.ts

import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ConfirmationDialog } from './confirm-dialog/confirmation-dialog';

@NgModule({
  imports: [
    ...
    FormsModule,
    ReactiveFormsModule
  ],
  declarations: [
    ...
    ConfirmationDialog
  ],
  providers: [ ... ],
  bootstrap: [ AppComponent ],
  entryComponents: [ConfirmationDialog]
})
export class AppModule { }

confirmation-dialog.ts

import { Component, Input } from '@angular/core';
import { MdDialog, MdDialogRef } from '@angular/material';

@Component({
  selector: 'confirm-dialog',
  templateUrl: '/app/confirm-dialog/confirmation-dialog.html',
})
export class ConfirmationDialog {
  constructor(public dialogRef: MdDialogRef<ConfirmationDialog>) {}

  public confirmMessage:string;
}

confirmation-dialog.html

<h1 md-dialog-title>Confirm</h1>
<div md-dialog-content>{{confirmMessage}}</div>
<div md-dialog-actions>
  <button md-button style="color: #fff;background-color: #153961;" (click)="dialogRef.close(true)">Confirm</button>
  <button md-button (click)="dialogRef.close(false)">Cancel</button>
</div>

app.component.html

<button (click)="openConfirmationDialog()">Delete me</button>

app.component.ts

import { MdDialog, MdDialogRef } from '@angular/material';
import { ConfirmationDialog } from './confirm-dialog/confirmation-dialog';

@Component({
  moduleId: module.id,
  templateUrl: '/app/app.component.html',
  styleUrls: ['/app/main.css']
})

export class AppComponent implements AfterViewInit {
  dialogRef: MdDialogRef<ConfirmationDialog>;

  constructor(public dialog: MdDialog) {}

  openConfirmationDialog() {
    this.dialogRef = this.dialog.open(ConfirmationDialog, {
      disableClose: false
    });
    this.dialogRef.componentInstance.confirmMessage = "Are you sure you want to delete?"

    this.dialogRef.afterClosed().subscribe(result => {
      if(result) {
        // do confirmation actions
      }
      this.dialogRef = null;
    });
  }
}

index.html => added following stylesheet

<link rel="stylesheet" href="node_modules/@angular/material/core/theming/prebuilt/indigo-pink.css">
Philip John
  • 5,275
  • 10
  • 43
  • 68
  • and where is `MdDialogRef` defined and how `dialogRef` is assigned in `AppComponent`? – Reza Jun 19 '17 at 15:59
  • @RezaRahmati As you can see in app.component.ts, MdDialogRef is defined in the node module @angular/material and dialogRef is assigned in openConfirmationDialog() method – Philip John Jun 20 '17 at 16:33
  • 1
    I also had to import **BrowserAnimationsModule** from @angular/platform-browser/animations and **MaterialModule** from @angular/material – hestellezg Sep 07 '17 at 17:36
  • 1
    Also, for styles I used `@import '~@angular/material/prebuilt-themes/deeppurple-amber.css';` following [this](https://material.angular.io/guide/theming) – hestellezg Sep 07 '17 at 20:58
  • 2
    back one year later, forgot to add a solved. Currently i'm in a big project and i opted for the matDialog wich is complete, easy to use but yet powerfull and gives you an easy way to comunicate between your main component and the dialog allowing so many new options. If anyone has this problem i recomend you to give it a try, it requires some preparation but it is worth as it works flawlessly – victor dencowski Feb 16 '18 at 20:51
  • 1
    Will this work with a call back function? I haven't tried the code, reading it I see only a statically typed callback e.g if you want to do a dynamic call like delete something by ID, you would need to parse a param and install the logics there. – KeaganFouche Apr 23 '18 at 14:26
  • This works very nice, but I was wondering if there is away for `openConfirmationDialog()` to return a value? Would it have to be an observable then due to the fact that it would be async because you would have to wait for the user input first? But would an observable that already has an observable inside of it be possible (meaning `openConfirmationDialog()` is already itself subscribed to `afterClosed`)? – Alfa Bravo Aug 15 '18 at 10:23
  • 1
    @AlfaBravo - I think a way (which I use anyway to unsubscribe from the afterClose subscription) is to return the Subscription. If the parent component needs a value from the confirmation dialog, it can just subscribe to the Subscription and get it. – Alexei - check Codidact Aug 29 '18 at 14:45
  • 4
    +1. This is a very good example that works almost out of the box. For the latest version, just replace `md` with `mat` and maybe use some built in styles for buttons: `mat-raised-button color="primary"` – Alexei - check Codidact Aug 29 '18 at 14:46
46

you can use window.confirm inside your function combined with if condition

 delete(whatever:any){
    if(window.confirm('Are sure you want to delete this item ?')){
    //put your delete method logic here
   }
}

when you call the delete method it will popup a confirmation message and when you press ok it will perform all the logic inside the if condition.

Tarek Badr
  • 847
  • 9
  • 12
27

I'm pretty late to the party, but here is another implementation using : https://stackblitz.com/edit/angular-confirmation-dialog

confirmation-dialog.service.ts

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

import { NgbModal } from '@ng-bootstrap/ng-bootstrap';

import { ConfirmationDialogComponent } from './confirmation-dialog.component';

@Injectable()
export class ConfirmationDialogService {

  constructor(private modalService: NgbModal) { }

  public confirm(
    title: string,
    message: string,
    btnOkText: string = 'OK',
    btnCancelText: string = 'Cancel',
    dialogSize: 'sm'|'lg' = 'sm'): Promise<boolean> {
    const modalRef = this.modalService.open(ConfirmationDialogComponent, { size: dialogSize });
    modalRef.componentInstance.title = title;
    modalRef.componentInstance.message = message;
    modalRef.componentInstance.btnOkText = btnOkText;
    modalRef.componentInstance.btnCancelText = btnCancelText;

    return modalRef.result;
  }

}

confirmation-dialog.component.ts

import { Component, Input, OnInit } from '@angular/core';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';

@Component({
  selector: 'app-confirmation-dialog',
  templateUrl: './confirmation-dialog.component.html',
  styleUrls: ['./confirmation-dialog.component.scss'],
})
export class ConfirmationDialogComponent implements OnInit {

  @Input() title: string;
  @Input() message: string;
  @Input() btnOkText: string;
  @Input() btnCancelText: string;

  constructor(private activeModal: NgbActiveModal) { }

  ngOnInit() {
  }

  public decline() {
    this.activeModal.close(false);
  }

  public accept() {
    this.activeModal.close(true);
  }

  public dismiss() {
    this.activeModal.dismiss();
  }

}

confirmation-dialog.component.html

<div class="modal-header">
  <h4 class="modal-title">{{ title }}</h4>
    <button type="button" class="close" aria-label="Close" (click)="dismiss()">
      <span aria-hidden="true">&times;</span>
    </button>
  </div>
  <div class="modal-body">
    {{ message }}
  </div>
  <div class="modal-footer">
    <button type="button" class="btn btn-danger" (click)="decline()">{{ btnCancelText }}</button>
    <button type="button" class="btn btn-primary" (click)="accept()">{{ btnOkText }}</button>
  </div>

Use the dialog like this:

public openConfirmationDialog() {
    this.confirmationDialogService.confirm('Please confirm..', 'Do you really want to ... ?')
    .then((confirmed) => console.log('User confirmed:', confirmed))
    .catch(() => console.log('User dismissed the dialog (e.g., by using ESC, clicking the cross icon, or clicking outside the dialog)'));
  }
tilo
  • 14,009
  • 6
  • 68
  • 85
  • I have followed the above code but when i click the button, the pop up modal comes for a fraction of second & disappear immediately. Can you help me out to fix this issue? – watraplion Apr 10 '18 at 09:43
  • Does this happen with the linked StackBlitz demo, too? – tilo Apr 10 '18 at 10:14
  • No.. In StackBlitz its working fine. I have copied all these files in my project template, where i am facing this issue – watraplion Apr 10 '18 at 10:16
  • Well, this is hard (read: impossible) to debug without further information. – tilo Apr 10 '18 at 11:20
  • @tilo: It looks like it only works in Angular 5. Not working in Angular 4. And more important they have removed @Injectable() in later versions of ng-bootstrap – SK. Apr 17 '18 at 16:16
  • @SK Works for me with Angular4: https://stackblitz.com/edit/angular-confirmation-dialog-v2uz7t And the @Injectable() is attached to the component, which shouldn't cause any issues. – tilo Apr 18 '18 at 09:53
  • @tilo and what If i have one textbox in the dialog box and I want to get that textbox value in the component where it is called? this is the question i have raised => https://stackoverflow.com/questions/66171661/how-to-get-data-of-input-in-confirm-dialog-box –  Feb 15 '21 at 05:31
  • @techguy Instead of returning true/false (see accept()/decline()), you can return any object, e.g. the textbox value. Note that the return value of the confirm methof of the service changes from Promise to Promise<{ whatever you return here}> – tilo Feb 15 '21 at 08:09
  • @tilo I tried but I am getting only true and false only. –  Feb 15 '21 at 08:17
  • how can I pass HTML string to message? Example: this.confirmationDialogService.confirm('Please confirm..', 'Do you
    really want to ... ?')
    – developer Oct 01 '21 at 15:00
18

UPDATE: Plunkr added

I was looking for a solution on all forums but found none, so found a solution with Old School Javascript Callback function. This is a really simple and clean way to create a confirmation dialog and set Callback functions for both YES and NO click events.
I have used Bootstrap CSS for Modal and An Alert Service with rxjs Subject.

alert.component.html

        <div *ngIf="message.type == 'confirm'"  class="modal-body">
            <div class="row">
                <div class="col-md-12">
                    <h3 class="text-center">{{message.text}}</h3>
                </div>
            </div>
            <div class="row">
                <div class="col-md-12">
                    <p class="text-center">
                        <a (click)="message.noFn()">
                            <button class="btn btn-pm">No</button>
                        </a>
                        <a (click)="message.siFn()">
                            <button  class="btn btn-sc" >Yes</button>
                        </a>
                    </p>
                </div>
            </div>
         </div>

alert.component.ts

export class AlertComponent {
    message: any;
    constructor(
      public router: Router, 
      private route: ActivatedRoute, 
      private alertService: AlertService,
   ) { }
   ngOnInit() {
    //this function waits for a message from alert service, it gets 
    //triggered when we call this from any other component
    this.alertService.getMessage().subscribe(message => {
        this.message = message;
    });
}

The most important part is here alert.service.ts

     import { Injectable } from '@angular/core'; 
     import { Router, NavigationStart } from '@angular/router'; 
     import { Observable } from 'rxjs'; 
     import { Subject } from 'rxjs/Subject';
     @Injectable() export class AlertService {
          private subject = new Subject<any>();
          constructor(){}
          confirm(message: string,siFn:()=>void,noFn:()=>void){
            this.setConfirmation(message,siFn,noFn);
          }
          setConfirmation(message: string,siFn:()=>void,noFn:()=>void) {
            let that = this;
            this.subject.next({ type: "confirm",
                        text: message,
                        siFn:
                        function(){
                            that.subject.next(); //this will close the modal
                            siFn();
                        },
                        noFn:function(){
                            that.subject.next();
                            noFn();
                        }
                     });

                 }

          getMessage(): Observable<any> {
             return this.subject.asObservable();
          }
       }

Call the function from any component

this.alertService.confirm("You sure Bro?",function(){
    //ACTION: Do this If user says YES
},function(){
    //ACTION: Do this if user says NO
})

Plunkr https://embed.plnkr.co/vWBT2nWmtsXff0MXMKdd/

ssuperczynski
  • 3,190
  • 3
  • 44
  • 61
Sudeep Rane
  • 331
  • 3
  • 8
  • hi.I'm looking for something like this ..but the code above not working for me.no modal shown and no error in console.tanks – mrapi Oct 31 '17 at 07:17
  • 1
    Hi Mrpi, Can you make sure if you have Bootstrap css in place? Now if you wanna test how your Modal looks remove ng-if and check. – Sudeep Rane Nov 01 '17 at 09:03
  • It works.thanks.what about positioning vertically to center? – mrapi Nov 01 '17 at 11:50
  • 1
    You need to perform some JS Calculation to do it vertically, cannot do it by CSS alone. But for Horizontally margin: 0 auto works like a charm! – Sudeep Rane Nov 03 '17 at 07:40
  • @NasiruddinSaiyed: could you please tell what modules to install. I am getting warning: WARNING in ./node_modules/@ng-bootstrap/ng-bootstrap/datepicker/datepicker-i18n.js 32:39-56 "export 'getLocaleDayNames' was not found in '@angular/common' – SK. Apr 16 '18 at 20:47
  • @SK i am not getting what you want to open please provide me your SO question link or a plunker – Nasiruddin Saiyed Apr 17 '18 at 12:13
  • @NasiruddinSaiyed: Never mind, I got it worked. That means I am able to see the confirm dialog. But I am not getting $http inside the function. I have asked a question here: https://stackoverflow.com/questions/49882736/angular-4-cannot-read-property-http-of-undefined/ – SK. Apr 17 '18 at 16:15
13

You could use sweetalert: https://sweetalert.js.org/guides/

npm install sweetalert --save

Then, simply import it into your application:

import swal from 'sweetalert';

If you pass two arguments, the first one will be the modal's title, and the second one its text.

swal("Here's the title!", "...and here's the text!");
Lars Rødal
  • 809
  • 1
  • 9
  • 26
6

Here's a slghtly different take using javascript's native confirm functionality and a custom Angular directive. It's super flexible and pretty lightweight:

Usage:

<button (hrsAreYouSure) (then)="confirm(arg1)" (else)="cancel(arg2)">
  This will execute confirm if user presses Ok on the confirmation dialog, or cancel if they
  hit Cancel
</button>

Directive:

import {Directive, ElementRef, EventEmitter, Inject, OnInit, Output} from '@angular/core';

@Directive({
  selector: '[hrsAreYouSure]'
})

export class AreYouSureDirective implements OnInit {

  @Output() then = new EventEmitter<boolean>();
  @Output() else = new EventEmitter<boolean>();

  constructor(@Inject(ElementRef) private element: ElementRef) { }

  ngOnInit(): void {
    const directive = this;
    this.element.nativeElement.onclick = function() {
      const result = confirm('Are you sure?');
      if (result) {
        directive.then.emit(true);
      } else {
        directive.else.emit(true);
      }
    };
  }
}
Chris HG
  • 1,412
  • 16
  • 20
  • Chrome required me to remove the parentheses around hrsAreYouSure in the button or it wouldn't work. – Reid Dec 30 '18 at 07:27
  • Also, if you press enter while the dialog box is up, you are confirming it whether that was your intent or not. – Reid Dec 30 '18 at 07:38
3

Adding more options to the answer.

You could use npm i sweetalert2

Don't forget to add the style to your angular.json

"styles": [
         ...
          "node_modules/sweetalert2/src/sweetalert2.scss"
          ]

Then just import,

// ES6 Modules or TypeScript
import Swal from 'sweetalert2'


// CommonJS
const Swal = require('sweetalert2')

Boom, you are ready to go.

Swal.fire({
  title: 'Are you sure?',
  text: 'You will not be able to recover this imaginary file!',
  icon: 'warning',
  showCancelButton: true,
  confirmButtonText: 'Yes, delete it!',
  cancelButtonText: 'No, keep it'
}).then((result) => {
  if (result.value) {
    Swal.fire(
      'Deleted!',
      'Your imaginary file has been deleted.',
      'success'
    )
  // For more information about handling dismissals please visit
  // https://sweetalert2.github.io/#handling-dismissals
  } else if (result.dismiss === Swal.DismissReason.cancel) {
    Swal.fire(
      'Cancelled',
      'Your imaginary file is safe :)',
      'error'
    )
  }
})

More on this:- https://www.npmjs.com/package/sweetalert2

I do hope this helps someone.

Thanks.

Anjana Silva
  • 8,353
  • 4
  • 51
  • 54
3

Always is better to make functionality and features to suit your specific project needs, but we often do not have time to code all the little things that we would like to. If you don't want to code it for yourself, it could be accomplished easily with the npm package @costlydeveloper/ngx-awesome-popup.

You could implement only Confirm Box module from the package in this way:

In your app.module.ts

    import {NgModule} from '@angular/core';
    import {BrowserModule} from '@angular/platform-browser';
    import {AppComponent} from './app.component';
    
    // Import your library
    import {ConfirmBoxConfigModule, NgxAwesomePopupModule} from '@costlydeveloper/ngx-awesome-popup';
    
    
    @NgModule({
        declarations: [
            AppComponent
        ],
        imports     : [
            BrowserModule,
    
            // Import popup and confirm box module
            NgxAwesomePopupModule.forRoot(),
            ConfirmBoxConfigModule.forRoot()
        ],
        providers   : [],
        bootstrap   : [AppComponent]
    })
    export class AppModule {
    }

Then evoke it anywhere in typescript code from this kind of method:

     confirmBox() {
            const confirmBox = new ConfirmBoxInitializer();
            confirmBox.setTitle('Are you sure?');
            confirmBox.setMessage('Confirm to delete user: John Doe!');
            // Set button labels, the first argument for the confirmation button, and the second one for the decline button.
            confirmBox.setButtonLabels('YES', 'NO');
            
            confirmBox.setConfig({
                DisableIcon: true, // optional
                AllowHTMLMessage: false, // optional
                ButtonPosition: 'center', // optional
                // Evoke the confirmation box with predefined types.
                LayoutType: DialogLayoutDisplay.DANGER // SUCCESS | INFO | NONE | DANGER | WARNING
            });
            
            // Simply evoke the popup and listen which button is clicked.
            const subscription = confirmBox.openConfirmBox$().subscribe(resp => {
                // IConfirmBoxPublicResponse
                console.log('ConfirmBox button response: ', resp);
                subscription.unsubscribe();
            });
        }

Get result like this:

enter image description here

1

In order to reuse a single confirmation dialog implementation in a multi-module application, the dialog must be implemented in a separate module. Here's one way of doing this with Material Design and FxFlex, though both of those can be trimmed back or replaced.

First the shared module (./app.module.ts):

import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {MatDialogModule, MatSelectModule} from '@angular/material';
import {ConfirmationDlgComponent} from './confirmation-dlg.component';
import {FlexLayoutModule} from '@angular/flex-layout';

@NgModule({
   imports: [
      CommonModule,
      FlexLayoutModule,
      MatDialogModule
   ],
   declarations: [
      ConfirmationDlgComponent
   ],
   exports: [
      ConfirmationDlgComponent
   ],
   entryComponents: [ConfirmationDlgComponent]
})

export class SharedModule {
}

And the dialog component (./confirmation-dlg.component.ts):

import {Component, Inject} from '@angular/core';
import {MAT_DIALOG_DATA} from '@angular/material';

@Component({
   selector: 'app-confirmation-dlg',
   template: `
      <div fxLayoutAlign="space-around" class="title colors" mat-dialog-title>{{data.title}}</div>
      <div class="msg" mat-dialog-content>
         {{data.msg}}
      </div>
      <a href="#"></a>
      <mat-dialog-actions fxLayoutAlign="space-around">
         <button mat-button [mat-dialog-close]="false" class="colors">No</button>
         <button mat-button [mat-dialog-close]="true" class="colors">Yes</button>
      </mat-dialog-actions>`,
   styles: [`
      .title {font-size: large;}
      .msg {font-size: medium;}
      .colors {color: white; background-color: #3f51b5;}
      button {flex-basis: 60px;}
   `]
})
export class ConfirmationDlgComponent {
   constructor(@Inject(MAT_DIALOG_DATA) public data: any) {}
}

Then we can use it in another module:

import {FlexLayoutModule} from '@angular/flex-layout';
import {NgModule} from '@angular/core';
import {GeneralComponent} from './general/general.component';
import {NgbModule} from '@ng-bootstrap/ng-bootstrap';
import {CommonModule} from '@angular/common';
import {MaterialModule} from '../../material.module';

@NgModule({
   declarations: [
      GeneralComponent
   ],
   imports: [
      FlexLayoutModule,
      MaterialModule,
      CommonModule,
      NgbModule.forRoot()
   ],
   providers: []
})
export class SystemAdminModule {}

The component's click handler uses the dialog:

import {Component} from '@angular/core';
import {ConfirmationDlgComponent} from '../../../shared/confirmation-dlg.component';
import {MatDialog} from '@angular/material';

@Component({
   selector: 'app-general',
   templateUrl: './general.component.html',
   styleUrls: ['./general.component.css']
})
export class GeneralComponent {

   constructor(private dialog: MatDialog) {}

   onWhateverClick() {
      const dlg = this.dialog.open(ConfirmationDlgComponent, {
         data: {title: 'Confirm Whatever', msg: 'Are you sure you want to whatever?'}
      });

      dlg.afterClosed().subscribe((whatever: boolean) => {
         if (whatever) {
            this.whatever();
         }
      });
   }

   whatever() {
      console.log('Do whatever');
   }
}

Just using the this.modal.open(MyComponent); as you did won't return you an object whose events you can subscribe to which is why you can't get it to do something. This code creates and opens a dialog whose events we can subscribe to.

If you trim back the css and html this is really a simple component, but writing it yourself gives you control over its design and layout whereas a pre-written component will need to be much more heavyweight to give you that control.

Stephen Turner
  • 7,125
  • 4
  • 51
  • 68
Lars H
  • 49
  • 6