We wrap in a Promise modal confirmation window

When the user performs some critical and / or irreversible actions, before sending a request to the server, you need to ask the user for confirmation.

As a rule, the โ€œAre you sure you want to do this and that and thatโ€ modal is displayed and there are two buttons below: Yes and No. If the user clicked โ€œyesโ€, then we send a request to the server and close the modal. If not, just close the modal.

This is standard functionality that is commonly used in several places in a project. Also, when building up the projectโ€™s functionality, itโ€™s likely to add a few more places where modals with confirmation are needed. Therefore, in order to avoid duplication of code, unambiguously such a modal must be taken out in a separate component. To avoid the temptation to hammer crutches, this component should be as versatile and easy to use as possible.

Let's move from lyrics to action. To display the modal, we will use Bootstrap.

Actually my version of such a component:

yes-no-modal.component.html
<div bsModal #yesNoModal="bs-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
  <div class="modal-dialog modal-{{type}}" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h4 class="modal-title">{{title}}</h4>
        <button type="button" class="close" (click)="onNoClick()" aria-label="Close">
          <span aria-hidden="true">ร—</span>
        </button>
      </div>
      <div class="modal-body">
        <p>{{body}}</p>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" (click)="onNoClick()">{{noBtnText}}</button>
        <button type="button" class="btn btn-{{type}}" (click)="onYesClick()">{{yesBtnText}}</button>
      </div>
    </div><!-- /.modal-content -->
  </div><!-- /.modal-dialog -->
</div><!-- /.modal -->

yes-no-modal.component.ts
import {Component, Input, OnInit, ViewChild} from '@angular/core';
import {ModalDirective} from 'ngx-bootstrap/modal';

@Component({
  selector: 'app-yes-no-modal',
  templateUrl: './yes-no-modal.component.html',
  styleUrls: ['./yes-no-modal.component.css']
})
export class YesNoModalComponent implements OnInit {

  @ViewChild('yesNoModal') public yesNoModal: ModalDirective;

  @Input() private type = 'info';
  @Input() private title = '';
  @Input() private body = '';
  @Input() private yesBtnText = '';
  @Input() private noBtnText = '';

  constructor() { }

  ngOnInit(): void {
  }

  public showAsync(data = null): Promise<any> {
    return new Promise<any>((resolve, reject) => {
      this.yesNoModal.show();
      this.onYesClick = () => {
        this.yesNoModal.hide();
        resolve(data);
      };
      this.onNoClick = () => {
        this.yesNoModal.hide();
        reject(data);
      };
    });
  }

  private onYesClick(): any {}
  private onNoClick(): any {}

}

I put the warning title, warning text, color scheme / severity level (danger, info, warning) into the parameters, you can also redefine the label on the buttons.

To show the modal, call showAsync, into which we can transfer arbitrary data. We get the same data in resolve / reject.

Next, connect the modal in another component:

account.component.html
<app-yes-no-modal
  #deleteModal
  body="     ?"
  title=""
  type="danger"
></app-yes-no-modal>
<button (click)="delete()"></button>

account.component.ts
import {Component, OnInit, ViewChild} from '@angular/core';
import {YesNoModalComponent} from '../_common/yes-no-modal/yes-no-modal.component';
import {DeleteUserEndpoint} from '../../api/delete-user.endpoint';
import {ErrorService} from '../../services/error/error.service';

@Component({
  selector: 'app-account',
  templateUrl: './account.component.html',
  styleUrls: ['./account.component.css'],
  providers: [DeleteUserEndpoint]
})
export class AccountComponent implements OnInit {


  @ViewChild('deleteModal') public deleteModal: YesNoModalComponent;

  constructor (private deleteUserEndpoint: DeleteUserEndpoint) {
  }

  delete(data) {
    this.deleteModal.showAsync(data).then(result => {
      this.deleteUserEndpoint.execute(result);
    });
  }

  ngOnInit(): void {
  }
}

In my opinion, using such a solution looks as simple and readable as possible.

For comparison, when using EventEmitter, you will have to define two methods in each component - showDeleteModal (), which is called by clicking on the delete button, and the delete () method, which is called by the modal event. If you have several such modals in one component for different user actions, then the readability of the code will already suffer.

In the comments, as always, I expect constructive and reasonable criticism.

All Articles