Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 27.08.2018, 13:03
Интересующийся
Отправить личное сообщение для kotelok Посмотреть профиль Найти все сообщения от kotelok
 
Регистрация: 27.08.2018
Сообщений: 22

Закрытие окна ngx-bootstrap.
Подскажите, пожалуйста, по некоторым моментам реализации.

Есть простая модальная форма на базе 'ngx-bootstrap'.
export class AboutComponent implements OnInit {

  public result: Subject<any> = new Subject<any>();
  
  constructor(private activeModal: BsModalRef) {
  }

  ngOnInit() {
    this.result.subscribe(() => this.activeModal.hide());
  }
  
  //Вызывается по кнопке "Отмена" и про крестику в окне.
  cancelDialog() {
    this.result.next(null);
  }

  //Вызывает по кнопке "ОК".
  confirmDialog() {
    this.result.next("some data");
  }
}


И есть её вызов:
showAbout() {
    this.modalService.show(AboutComponent).content.result.subscribe((value) => {
      console.info(value);
    });    
  }


В целом, работает как и ожидается, но есть непонимание некоторых моментов:
1. Нужно ли отписываться от событий 'result' в обоих местах? Или, вероятно, нужно использовать другой тип объекта или другой тип подписки, при условии, что ожидается ровно одно событие и любая другая логика считается ошибочной.
2. Если отписываться не нужно, то вопрос закрыт. Если нужно, то п.3.
3. Как в рамках конкретного окна гарантировать вызов 'this.result.next(null)' не только по факту явного нажатия "отмены", но и когда 'bootstrap' сам закрывает окно? Например, если пользователь жмёт 'escape' или кликает мимо окна. Эти события можно заблокировать, но подписаться на них нельзя. Использовать 'BsModalService.onHide' не получается, т.к. в рамках этого события нет информации об источнике, его сгенерировавшем и состоянии 'result' соответствующего компонента. Использовать 'ngOnDestroy' самого компонента тоже неудобно, т.к. приходится реализовывать логику для определения каким именно путём мы попали в это событие для определения нужно ли устанавливать значение 'result' (или оно уже было установлено ранее).
Ответить с цитированием
  #2 (permalink)  
Старый 27.08.2018, 14:02
Аватар для destus
Профессор
Отправить личное сообщение для destus Посмотреть профиль Найти все сообщения от destus
 
Регистрация: 18.05.2011
Сообщений: 1,207

1. Отписываться нужно, ну или подписываться так
this.result
.pipe(
    take(1)
)
.subscribe()

2. onHide и нужно использовать, потому что он как раз и генерирует lastDismissReason.
https://stackblitz.com/edit/ngx-boot...p.component.ts
Ответить с цитированием
  #3 (permalink)  
Старый 27.08.2018, 14:14
Интересующийся
Отправить личное сообщение для kotelok Посмотреть профиль Найти все сообщения от kotelok
 
Регистрация: 27.08.2018
Сообщений: 22

С подпиской понял, спасибо.

С 'onHide' не очень понятно. Открыто модально окно, из него открыто другое модальное окно. В обоих подписались на 'BsModalService.onHide'. Закрылось последнее окно, событие получили оба подписчика. Как понять, чьё именно это событие, если в качестве параметра 'onHide' приходит просто строка с указанием причины закрытия или null (если закрыто кодом без указания причины)?

А если событие не перехватывать, получается, что при закрытии окна самим 'bootstrap', значение 'result' никогда не будет установлено, а значит и подписка, дажче через 'result.pipe(takie(1)).subscribe' зависнет в ожидании навсегда. Или такая подписка сбросится, если объект компонента будет уничтожен? Или сброщик мусора не будет уничтожать объект, на свойство которого кто-то подписан?

И есть ли какая-нибудь возможность отловить событие, когда сборщик мусора собирает объект? Какой-нибудь аналог деструктора из других языков? Тогда хоть попроще будет самостоятельно исследовать поведение.

Последний раз редактировалось kotelok, 27.08.2018 в 14:33.
Ответить с цитированием
  #4 (permalink)  
Старый 27.08.2018, 14:47
Аватар для destus
Профессор
Отправить личное сообщение для destus Посмотреть профиль Найти все сообщения от destus
 
Регистрация: 18.05.2011
Сообщений: 1,207

kotelok,
Цитата:
С 'onHide' не очень понятно. Открыто модально окно, из него открыто другое модальное окно. В обоих подписались на 'BsModalService.onHide'. Закрылось последнее окно, событие получили оба подписчика.
А вы подписывайтесь не в компоненте, который служит для контента модального окна, а в компоненте на уровень выше, т.е. в данном случае в том, где определен метод showAbout. Это позволит завести массив, openedWindows: BsModalRef[] и при открытии окна делать push инстанса BsModalRef, а при срабатывании подписки onHide всегда знать какое именно окно закрылось (всегда то, которое последним открылось, т.е. openedWindows[openedWindows.length - 1]), не забывая делать splice затем, чтобы поддерживать коллекцию в актуальном состоянии.
Цитата:
А если событие не перехватывать, получается, что при закрытии окна самим 'bootstrap', значение 'result' никогда не будет установлено, а значит и подписка, дажче через 'result.pipe(takie(1)).subscribe' зависнет в ожидании навсегда.
В данном случае лучше отписывать в ngOnDestroy
Цитата:
Или такая подписка сбросится, если объект компонента будет уничтожен?
нет
Цитата:
И есть ли какая-нибудь возможность отловить событие, когда сборщик мусора собирает объект?
нет
Ответить с цитированием
  #5 (permalink)  
Старый 27.08.2018, 15:45
Интересующийся
Отправить личное сообщение для kotelok Посмотреть профиль Найти все сообщения от kotelok
 
Регистрация: 27.08.2018
Сообщений: 22

Да, спасибо, вроде получилось, все сценарии обрабатываются для любого уровня вложенности.

Радует, что сам компонент диалога удалось избавить от всех зависимостей по управлению диалогами и необходимости явно вызывать 'hide'.

Вызывает некоторые сомнения, что при закрытии окна самим бутстрапом, будет происходить явный вызов 'hide' для уже закрытого окна - блок 'reason != null' отрабатывает если бутстрап сам закрыл окно, там происходит установка значения 'result', что провоцирует отработку события с закрытием окна в подписке в методе 'show'.

Можно ли как-то отписать именно текущего наблюдателя, чтобы подписка внешнего компонента на 'result' сработала, а локальная нет? Или как-то проверить, что окно уже закрыто?

Сервис, контролирующий открытие/закрытие окон:
@Injectable()
export class ModalWrapService {

    private activeModals: BsModalRef[] = [];

    constructor(public modalService: BsModalService) {

        this.modalService.onHide.subscribe(reason => {
            
            if (reason != null) {
                this.activeModals[this.activeModals.length - 1].content.result.next(null);
            }

            this.activeModals.splice(this.activeModals.length - 1, 1);
        });
    }

    show(content: string | TemplateRef<any> | any): BsModalRef {
        var modalRef = this.modalService.show(content);

        this.activeModals.push(modalRef);

        modalRef.content.result.pipe(take(1)).subscribe(() => {
            modalRef.hide();
        });

        return modalRef;
    }
}

Компонент:
export class AboutComponent {

  public result: Subject<string> = new Subject();

  cancelDialog() {
    this.result.next(null);
  }

  confirmDialog() {
    this.result.next("some user data");
  }
}

Вызов окна:
showAbout() {
  //Согласно документации, должен самоотписаться после первого события.
  this.modalWrapService.show(AboutComponent).content.result.pipe(take(1)).subscribe((value) => {
    console.info(value);
  });    
}
Ответить с цитированием
  #6 (permalink)  
Старый 27.08.2018, 17:10
Аватар для destus
Профессор
Отправить личное сообщение для destus Посмотреть профиль Найти все сообщения от destus
 
Регистрация: 18.05.2011
Сообщений: 1,207

в modalService можно вызвать метод getModalsCount, и если это значение не совпадает с вашим activeModals.length, то окно было закрыто самим бутстрапом...
Ответить с цитированием
  #7 (permalink)  
Старый 28.08.2018, 08:29
Интересующийся
Отправить личное сообщение для kotelok Посмотреть профиль Найти все сообщения от kotelok
 
Регистрация: 27.08.2018
Сообщений: 22

Да, работает, спасибо. Только не нашёл в документации, в какой именно момент обновляется значение, возвращаемое 'getModalsCount', так что немного иначе получилось, но суть та же:
@Injectable()
export class ModalWrapService {

    private activeModals: BsModalRef[] = [];

    constructor(private modalService: BsModalService) {

        this.modalService.onHide.subscribe(reason => {
            var topModalRef = this.activeModals.splice(this.activeModals.length - 1, 1)[0];

            if (reason != null) {
                topModalRef.content.result.next(null);
            }
        });
    }

    show(content: string | TemplateRef<any> | any, config?: ModalOptions): AnonymousSubject<any> {
        var modalRef = this.modalService.show(content, config);

        this.activeModals.push(modalRef);

        modalRef.content.result.pipe(take(1)).subscribe(() => {
            
            if (modalRef == (this.activeModals.length == 0 ? null : this.activeModals[this.activeModals.length - 1])) {
                modalRef.hide();
            }
        });

        return modalRef.content.result.pipe(take(1));
    }
}
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Привязать закрытие окна bootstrap при наатии только на определенную кнопку voron121 Библиотеки/Тулкиты/Фреймворки 0 15.08.2014 02:23
Закрытие модального окна bootstrap по [Esc] Yaroma11 Библиотеки/Тулкиты/Фреймворки 5 22.05.2014 11:22
Событие на закрытие окна браузера пользователем Stilus Events/DOM/Window 3 18.01.2014 00:42
Закрытие модального окна от bootstrap carroty jQuery 0 11.10.2013 01:54
Обновление страница при закрытие мод. окна Юсуф Events/DOM/Window 2 14.06.2013 07:32