Закрытие окна 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' (или оно уже было установлено ранее). |
1. Отписываться нужно, ну или подписываться так
this.result .pipe( take(1) ) .subscribe() 2. onHide и нужно использовать, потому что он как раз и генерирует lastDismissReason. https://stackblitz.com/edit/ngx-boot...p.component.ts |
С подпиской понял, спасибо.
С 'onHide' не очень понятно. Открыто модально окно, из него открыто другое модальное окно. В обоих подписались на 'BsModalService.onHide'. Закрылось последнее окно, событие получили оба подписчика. Как понять, чьё именно это событие, если в качестве параметра 'onHide' приходит просто строка с указанием причины закрытия или null (если закрыто кодом без указания причины)? А если событие не перехватывать, получается, что при закрытии окна самим 'bootstrap', значение 'result' никогда не будет установлено, а значит и подписка, дажче через 'result.pipe(takie(1)).subscribe' зависнет в ожидании навсегда. Или такая подписка сбросится, если объект компонента будет уничтожен? Или сброщик мусора не будет уничтожать объект, на свойство которого кто-то подписан? И есть ли какая-нибудь возможность отловить событие, когда сборщик мусора собирает объект? Какой-нибудь аналог деструктора из других языков? Тогда хоть попроще будет самостоятельно исследовать поведение. |
kotelok,
Цитата:
Цитата:
Цитата:
Цитата:
|
Да, спасибо, вроде получилось, все сценарии обрабатываются для любого уровня вложенности.
Радует, что сам компонент диалога удалось избавить от всех зависимостей по управлению диалогами и необходимости явно вызывать '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); }); } |
в modalService можно вызвать метод getModalsCount, и если это значение не совпадает с вашим activeModals.length, то окно было закрыто самим бутстрапом...
|
Да, работает, спасибо. Только не нашёл в документации, в какой именно момент обновляется значение, возвращаемое '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)); } } |
Часовой пояс GMT +3, время: 08:20. |