Заворачиваем в Promise модальное окно подтверждения действия

—

от автора

Когда пользователь совершает какие-то критические и/или необратимые действия, перед тем, как отправить запрос на сервер, нужно запросить у пользователя подтверждение.

Как правило, выводится модал «Вы уверены, что хотите сделать то то и то то» и внизу две кнопки: Да и Нет. Если пользователь нажал «да», то отправляем запрос на сервер и закрываем модал. Если «нет», просто закрываем модал.

Это стандартный функционал, который обычно используется в нескольких местах в проекте. Также при наращивании функционала проекта, скорее всего добавиться еще несколько мест, где нужны модалы с подтверждением. Поэтому, во избежание дублирования кода, однозначно такой модал нужно выносить в отдельный компонент. Во избежание соблазна забивать костыли, этот компонент должен быть максимально универсальным и простым в использовании.

Перейдем от лирики к делу. Для отображения модала будем использовать Bootstrap.

Собственно мой вариант такого компонента:

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

В параметры вынес заголовок предупреждения, текст предупреждения, цветовую схему/уровень важности (danger, info, warning), также можно переопределить надпись на кнопках.

Для показа модала вызываем showAsync, в который можем передавать произвольные данные. Эти же данные получаем в resolve/reject.

Далее подключаем модал в другом компоненте:

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

На мой взгляд, использование такого решения выглядит максимально просто и читаемо.

Для сравнения, при использовании EventEmitter, придется определять два метода в каждом компоненте — showDeleteModal(), который вызывается кликом по кнопке удалить, и метод delete(), который вызывается по событию модала. Если у вас в одном компоненте будет несколько таких модалов на разные действия пользователя, то читаемость кода будет уже страдать.

В комментариях как всегда жду конструктивную и обоснованную критику.

ссылка на оригинал статьи https://habr.com/ru/post/502386/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *