Отменяемый компонент @Ouput EventEmitter?

Если у меня есть пользовательский компонент 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();
  }
}

Таким образом, ожидаемое поведение будет следующим:

  1. Что-то вызывает closePanel() в экземпляре <my-panel>.
  2. HomeComponent получает уведомление о том, что панель пытается закрыться, и может при желании отменить закрытие панели.
  3. Если ничто не отменяет его, экземпляр <my-panel> продолжит закрытие.

Я не уверен, с чего начать, но я думаю, что возможность использовать RxJS Observables была бы идеальной.

Обновлено: я знаю, что библиотека DevExtreme делает что-то подобное, я думаю, что нашел как они это реализуют, но мне пока не удалось обдумать это, и, вероятно, это больше, чем мне нужно...

Вы можете определить свою собственную функцию для обработки логики отмены закрытия и передать ее через событие @Output.

webdevius 07.03.2019 19:54

Проверьте альтернативную версию принятого ответа, используя собственный API CustomEvent: stackoverflow.com/a/70386214/3317037.

daymannovaes 05.01.2022 23:00
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
0
2
178
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Самым простым вариантом было бы, чтобы ваш компонент панели разделил закрытие на 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-запрос, прежде чем она сможет принять решение, это будет блокировать выполнение, верно?

Kyle V. 07.03.2019 20:25

Я не могу придумать ни одного, если только вы не передаете один и тот же экземпляр вашего PanelComponent и не имеете нескольких слушателей, привязанных к одному и тому же выводу, и предотвращаете закрытие по-разному (что было бы настоящим беспорядком, и его следует избегать). На самом деле это не эксплуатация - я считаю, что так и должно быть. Вы можете установить свой EventEmitter как Async, передав true при создании нового EventEmitter: new EventEmitter(true), если когда-либо понадобится

webdevius 07.03.2019 20:31

эй, проверьте эту улучшенную версию, используя собственный API CustomEvent: stackoverflow.com/a/70386214/3317037

daymannovaes 05.01.2022 22:59

Вот мое решение:

Создайте интерфейс 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

daymannovaes 16.12.2021 23:09

Основываясь на предыдущем ответе @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' }

Другие вопросы по теме