在Angular中制作确认对话框的简单方法?

125

有没有一种不那么复杂的方法在angular 2中创建确认对话框?这个想法是点击一个项目,然后显示一个弹出窗口或模态框来确认其删除,我尝试了这里的angular 2模态框angular2-modal,但我不知道如何让它在确认或取消时执行某些操作。单击函数运行良好,唯一的问题是我不太清楚如何使用它。我还有另一个使用相同插件但不同的模态框。

this.modal.open(MyComponent);

我不想为了显示确认框而创建另一个组件,所以我想问一下。


1
“简单的方式”是使用像你提到的 angular2-modal 这样的工具。否则,你就得自己构建一个,而我不认为那是一种简单的方式。你看到链接中提供了 Plunker 的示例展示了如何使用 :) http://embed.plnkr.co/mbPzd8/ - AT82
我明白了,那么创建一个带有确认弹出框的组件是个好主意,其中包含两个按钮,当点击时执行主组件中的函数。 在示例中,他使用keyup 5来关闭对话框,我需要关闭它并调用一个函数。无论如何,我会尽可能简单明了地制作并发布结果。 - victor dencowski
还有其他的模态框,可能具有您希望拥有的其他内置功能,但是您的要求似乎并不太困难,所以我猜很多都适合您,但如果您愿意,可以四处浏览并找到自己喜欢的。这里有一些例子:https://www.npmjs.com/search?q=angular+2+modal 尝试一下,如果遇到问题,请回到SO!祝编码愉快! :) - AT82
9个回答

271

方法一

确认的一个简单方式是使用本地浏览器的确认提示。 模板可以有一个按钮或链接。

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

组件方法可以像下面这样。

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

方法2

另一种获得简单确认对话框的方法是使用Angular Bootstrap组件,比如ng-bootstrapngx-bootstrap。您只需安装组件并使用模态组件即可。

  1. 使用ng-bootstrap的模态框示例
  2. 使用ngx-bootstrap的模态框示例

方法3

下面提供了另一个在我的项目中实现简单确认弹出窗口的方法,使用了angular2/material

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 { }

确认对话框.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;
}

确认对话框.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>

应用程序组件.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 => 加入以下样式表

<link rel="stylesheet" href="node_modules/@angular/material/core/theming/prebuilt/indigo-pink.css">

1
此外,对于样式,我使用了 @import '~@angular/material/prebuilt-themes/deeppurple-amber.css'; 以及遵循了这个指南。 - hestellezg
2
一年后回来,忘记添加已解决。目前我正在进行一个大项目,我选择了matDialog,它是完整的、易于使用但功能强大,并且为您提供了一种在主组件和对话框之间进行通信的简单方式,从而允许许多新选项。如果有人遇到这个问题,我建议你试试,它需要一些准备工作,但它值得,因为它可以无缝地工作。 - victor dencowski
1
这个能用回调函数吗?我还没有尝试过这段代码,但是从阅读中我只看到了一个静态类型的回调函数。例如,如果你想通过ID删除某些内容进行动态调用,你需要解析参数并在那里安装逻辑。 - KeaganFouche
1
@AlfaBravo - 我认为一种(我用来取消订阅afterClose订阅的方式)是返回Subscription。如果父组件需要确认对话框中的值,它只需订阅Subscription并获取它。 - Alexei - check Codidact
4
这是一个几乎可以马上使用的非常好的例子。如果想要使用最新版本,只需将 md 替换为 mat,并且可能使用一些内置的样式来创建按钮:mat-raised-button color="primary" - Alexei - check Codidact
显示剩余4条评论

47

你可以在函数内部结合 if 条件使用 window.confirm

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

当您调用 delete 方法时,它会弹出一个确认消息,当您按下“确定”按钮时,它将执行 if 条件内的所有逻辑。


3
您无需使用窗口关键字。 - Ambrus Tóth

27

我来晚了,但这里有另一个使用 的实现: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>

使用对话框的方式如下:

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)'));
  }

这个问题在链接的 StackBlitz 示例中也会发生吗? - tilo
不是的,在 StackBlitz 上它运行得很好。我已经将所有这些文件复制到我的项目模板中,但在那里我遇到了这个问题。 - watraplion
嗯,如果没有更多的信息,这很难(读作:不可能)进行调试。 - tilo
@tilo 我尝试了,但是我只得到了 true 和 false。 - user14922829
如何将HTML字符串传递给消息?例如:this.confirmationDialogService.confirm('请确认..', '你<br/>真的想要...吗?') - developer
显示剩余5条评论

18

更新:添加了Plunkr

我在所有论坛上都寻找解决方案,但是没有找到,所以找到了一种使用老式JavaScript回调函数的解决方案。 这是一种非常简单和干净的方法来创建确认对话框,并为YESNO点击事件设置回调函数。
我使用Bootstrap CSS用于模态窗口以及带有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;
    });
}

最重要的部分在这里: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();
          }
       }

从任何组件调用该函数

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/


嗨,我正在寻找类似于这样的东西..但是上面的代码对我不起作用。没有显示模态,也没有控制台错误。谢谢。 - mrapi
1
嗨,Mrpi,你能确保你已经安装了Bootstrap CSS吗?现在,如果你想测试一下你的模态框是什么样子,请移除ng-if并检查。 - Sudeep Rane
它可以运行。谢谢。那么如何将其垂直居中定位? - mrapi
1
您需要进行一些JS计算才能垂直完成它,无法仅通过CSS完成。但对于水平方向,margin: 0 auto非常好用! - Sudeep Rane
@SK,我不明白你想打开什么,请给我你的SO问题链接或一个Plunker。 - Nasiruddin Saiyed
显示剩余2条评论

13

您可以使用sweetalert:https://sweetalert.js.org/guides/

npm install sweetalert --save

然后,只需将其导入您的应用程序:

import swal from 'sweetalert';

如果您传递两个参数,第一个参数将成为模态框的标题,第二个参数将成为其文本。

swal("Here's the title!", "...and here's the text!");

6
这里有一个稍微不同的方法,使用JavaScript的原生确认功能和自定义的Angular指令。它非常灵活且相当轻量级:
用法:
<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>

指令:

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);
      }
    };
  }
}

Chrome 要求我在按钮中删除 hrsAreYouSure 周围的括号,否则它将无法工作。 - Reid
此外,如果在对话框弹出时按下回车键,则无论您是否打算这样做,都会确认它。 - Reid

3

为了使功能和特性适合您的具体项目需求,最好自己编写代码,但我们通常没有时间编写所有我们想要的小东西。如果您不想为自己编写代码,可以轻松地使用npm包@costlydeveloper/ngx-awesome-popup实现。

您可以通过以下方式仅实现该软件包中的确认框模块:

在您的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 {
    }

然后可以通过这种方法在 TypeScript 代码的任何地方调用它:

     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();
            });
        }

获得如下结果:

enter image description here


3

添加更多答案选项。

您可以使用npm i sweetalert2

别忘了在您的angular.json中添加样式。

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

然后只需导入即可。
// ES6 Modules or TypeScript
import Swal from 'sweetalert2'


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

瞧,你已经准备好了。

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'
    )
  }
})

更多信息请参考:https://www.npmjs.com/package/sweetalert2

希望这能帮助到某些人。

谢谢。


1
为了在多模块应用程序中重复使用单个确认对话框实现,必须在单独的模块中实现该对话框。以下是一种使用Material Design和FxFlex的方法,尽管两者都可以削减或替换。
首先是共享模块(./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 {
}

这是对话框组件(./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) {}
}

然后我们可以在另一个模块中使用它:

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 {}

组件的点击处理程序使用对话框:
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');
   }
}

只是像您所做的那样使用this.modal.open(MyComponent);并不会返回一个对象,您可以订阅其事件,这就是为什么您无法使其执行某些操作。此代码创建并打开一个对话框,我们可以订阅其事件。

如果你简化css和html,这实际上是一个简单的组件,但自己编写它可以让你控制它的设计和布局,而预先编写的组件将需要更重的权重来给你这种控制。


回答不错,但我认为将组件放在单独的共享模块中会增加不必要的复杂性。 - Stephen Turner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接