Если у меня есть пользовательский компонент Angular, скажем, MyPanelComponent/<my-panel>, как я могу реализовать функциональность, где, если что-то вызывает MyPanelComponent.closePanel(), я могу позволить чему-то другому остановить закрытие панели?
Я имею в виду что-то вроде этого:
home.component.html:
<my-panel (closing) = "panelIsClosing($event)"> ... </my-panel>
home.component.ts
panelIsClosing(event: any) {
if (!allowPanelClose) {
event.stopPropagation();
}
}
Таким образом, ожидаемое поведение будет следующим:
closePanel() в экземпляре <my-panel>.HomeComponent получает уведомление о том, что панель пытается закрыться, и может при желании отменить закрытие панели.<my-panel> продолжит закрытие.Я не уверен, с чего начать, но я думаю, что возможность использовать RxJS Observables была бы идеальной.
Обновлено: я знаю, что библиотека DevExtreme делает что-то подобное, я думаю, что нашел как они это реализуют, но мне пока не удалось обдумать это, и, вероятно, это больше, чем мне нужно...
Проверьте альтернативную версию принятого ответа, используя собственный API CustomEvent: stackoverflow.com/a/70386214/3317037.





Самым простым вариантом было бы, чтобы ваш компонент панели разделил закрытие на 2 разных этапа. Событие близко и метод, который фактически его закрывает.
Это будет выглядеть примерно так:
import { Component, OnInit, EventEmitter, Output, Input } from '@angular/core';
@Component({
selector: 'app-confirmable',
templateUrl: './confirmable.component.html',
styleUrls: ['./confirmable.component.css']
})
export class ConfirmableComponent implements OnInit {
@Output()
public close = new EventEmitter();
@Input()
public text: string;
constructor() { }
ngOnInit() {
}
public actualClose() {
console.info('actual close logic');
}
public notifyClosure() {
this.close.emit();
}
}
<button (click) = "notifyClosure()">{{text}}</button>
Затем, где он использовался, вы можете решить, хотите ли вы на самом деле закрыть его или нет.
<app-confirmable text = "I will never go away" (close) = "doNotClose()"></app-confirmable>
<app-confirmable #confirmable text = "I will never go away"(close) = "confirmable.actualClose()"></app-confirmable>
Если вам нужна ссылка внутри контроллера, вы можете получить ее с помощью @ViewChild.
Здесь — минимальный рабочий пример.
В вашем PanelComponent вы можете использовать тот факт, что поведение EventEmitter по умолчанию является синхронным *
close() {
this.closing.emit({
preventClose: () => {
this.closePrevented = true
}
});
if (this.closePrevented) {
return;
}
// Go on with closing implementation
}
Очень простая/базовая демонстрация здесь.
* Ссылка: Документы EventEmitter
Это похоже на то решение, которое я искал. Есть ли недостатки в использовании того факта, что EventEmitter является синхронным? Я могу сразу подумать об одном: если логика, чтобы решить, закрывать или нет, должна сделать HTTP-запрос, прежде чем она сможет принять решение, это будет блокировать выполнение, верно?
Я не могу придумать ни одного, если только вы не передаете один и тот же экземпляр вашего PanelComponent и не имеете нескольких слушателей, привязанных к одному и тому же выводу, и предотвращаете закрытие по-разному (что было бы настоящим беспорядком, и его следует избегать). На самом деле это не эксплуатация - я считаю, что так и должно быть. Вы можете установить свой EventEmitter как Async, передав true при создании нового EventEmitter: new EventEmitter(true), если когда-либо понадобится
эй, проверьте эту улучшенную версию, используя собственный API CustomEvent: stackoverflow.com/a/70386214/3317037
Вот мое решение:
Создайте интерфейс CancelableEvent, запустите его и проверьте, отменено ли оно, прежде чем что-то делать.
export interface CancelableEvent<T> {
data:T;
canceled : boolean;
}
// fire event:
let evt = ... // create CancelableEvent;
this.myEvent$.emit(evt);
if (evt.canceled) return; // check if it's canceled
...
// when subscribe event:
if (evt.canceled) throw 'event canceled by others'; // check if it's canceled
if (xxx) {
evt.canceled = true;
throw 'event canceled by me' // ignore following business logic
}
// business logic here
Я сделал небольшое улучшение, используя собственный CustomEvent здесь: stackoverflow.com/a/70386214/3317037
Основываясь на предыдущем ответе @outersky, даже лучше, если бы вы могли использовать уже стандартный API собственного CustomEvent и сделать что-то вроде этого:
export class CancellableEvent<T> extends CustomEvent<T> {
detail: T;
constructor(detail: T) {
super('cancellableEvent', {
detail,
cancelable: true
});
}
}
const event = new CancellableEvent({ some: 'property' });
event.defaultPrevented; // returns false
event.preventDefault();
event.defaultPrevented; // returns true
event.detail; // returns { some: 'property' }
Вы можете определить свою собственную функцию для обработки логики отмены закрытия и передать ее через событие @Output.