Когда пользователь совершает какие-то критические и/или необратимые действия, перед тем, как отправить запрос на сервер, нужно запросить у пользователя подтверждение.
Как правило, выводится модал «Вы уверены, что хотите сделать то то и то то» и внизу две кнопки: Да и Нет. Если пользователь нажал «да», то отправляем запрос на сервер и закрываем модал. Если «нет», просто закрываем модал.
Это стандартный функционал, который обычно используется в нескольких местах в проекте. Также при наращивании функционала проекта, скорее всего добавиться еще несколько мест, где нужны модалы с подтверждением. Поэтому, во избежание дублирования кода, однозначно такой модал нужно выносить в отдельный компонент. Во избежание соблазна забивать костыли, этот компонент должен быть максимально универсальным и простым в использовании.
Перейдем от лирики к делу. Для отображения модала будем использовать Bootstrap.
Собственно мой вариант такого компонента:
yes-no-modal.component.html
yes-no-modal.component.ts
В параметры вынес заголовок предупреждения, текст предупреждения, цветовую схему/уровень важности (danger, info, warning), также можно переопределить надпись на кнопках.
Для показа модала вызываем showAsync, в который можем передавать произвольные данные. Эти же данные получаем в resolve/reject.
Далее подключаем модал в другом компоненте:
account.component.html
account.component.ts
На мой взгляд, использование такого решения выглядит максимально просто и читаемо.
Для сравнения, при использовании EventEmitter, придется определять два метода в каждом компоненте — showDeleteModal(), который вызывается кликом по кнопке удалить, и метод delete(), который вызывается по событию модала. Если у вас в одном компоненте будет несколько таких модалов на разные действия пользователя, то читаемость кода будет уже страдать.
В комментариях как всегда жду конструктивную и обоснованную критику.
Как правило, выводится модал «Вы уверены, что хотите сделать то то и то то» и внизу две кнопки: Да и Нет. Если пользователь нажал «да», то отправляем запрос на сервер и закрываем модал. Если «нет», просто закрываем модал.
Это стандартный функционал, который обычно используется в нескольких местах в проекте. Также при наращивании функционала проекта, скорее всего добавиться еще несколько мест, где нужны модалы с подтверждением. Поэтому, во избежание дублирования кода, однозначно такой модал нужно выносить в отдельный компонент. Во избежание соблазна забивать костыли, этот компонент должен быть максимально универсальным и простым в использовании.
Перейдем от лирики к делу. Для отображения модала будем использовать 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(), который вызывается по событию модала. Если у вас в одном компоненте будет несколько таких модалов на разные действия пользователя, то читаемость кода будет уже страдать.
В комментариях как всегда жду конструктивную и обоснованную критику.
reforms
1) Почему не используете async, await? Ваш код будет гораздо проще.
2) Из своего опыта скажу, что делать reject на отмену действия пользователя — не самый удачный вариант. Бывает так, что в коде идет последовательная цепочка действий, при которой, если произойдет ошибка необходимо режектить. И тогда может случиться каша из состояний — то ли отмена пользователя, то ли прикладная ошибка. Суть думаю уловили.
Но идею с обобщением решения поддерживаю. Я использую чаще такую конструкцию, если переложить ее на Ваш пример
Пример выше без обработок ошибок, так ак это другая история
atomic1989
Как чистить колбэки? Я бы использовал rxjs. Позволяет контролировать потоки и отменять их, с промисами не видел адекватного решения, пусть код на async await и выглядит приятнее
iltimiroff
Плюсую, использовал похожий подход в реакт + редакс, выглядело примерно так же. Очень удобно