Last active
October 9, 2018 19:26
-
-
Save ValentinFunk/5f1aafae2af46d42d2804ad03411862f to your computer and use it in GitHub Desktop.
@ngrx/store and @ng-bootstrap/ng-bootstrap NgbModal
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Injectable, Component, ɵisObservable as isObservable, Type } from '@angular/core'; | |
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; | |
import { Store, Action } from '@ngrx/store'; | |
import { Subscription } from 'rxjs/Subscription'; | |
import { Observable } from 'rxjs/Observable'; | |
type SelectFn = (appState: AppState) => boolean; | |
type ActionCreatorFn = (reason: string) => Action; | |
/** | |
* Keep Modal in Sync with Application State. | |
* (A bit hacky because ngbootstrap modal wasn't built for ngrx/Store) | |
*/ | |
@Injectable() | |
export class ModalService { | |
private modals: { | |
[index: string]: NgbModalRef | |
} = {}; | |
constructor( | |
private modalService: NgbModal, | |
private store: Store<any> | |
) {} | |
/** | |
* Add a modal listener. | |
* | |
* @param id Unique id for this component. | |
* @param component Component type to instantiate. | |
* @param selectFn Selector selecting a boolean state attribute that determines modal visibility. | |
* @param closeAction Action creator, this action is fired if the modal is closed. | |
*/ | |
addModalListener( | |
id: string, | |
component: Type<any>, | |
selectFn: SelectFn, | |
closeAction: ActionCreatorFn): Subscription; | |
/** | |
* Add a modal listener for a component of a lazy-loaded module. | |
* | |
* @param id Unique id for this component. | |
* @param component Component type to instantiate. | |
* @param selectFn Selector selecting a boolean state attribute that determines modal visibility. | |
* @param closeAction Action creator, this action is fired if the modal is closed. | |
* @param lazyModalService NgbModal instance of the lazy loaded module. | |
*/ | |
addModalListener( | |
id: string, | |
component: Type<any>, | |
selectFn: SelectFn, | |
closeAction: ActionCreatorFn, | |
lazyModalService: NgbModal): Subscription; | |
addModalListener( | |
id: string, | |
component: Type<any>, | |
selectFn: SelectFn, | |
closeAction: ActionCreatorFn, | |
lazyModalService?: NgbModal, | |
): Subscription { | |
lazyModalService = lazyModalService || this.modalService; | |
return this.addModalListenerEx(id, component, selectFn, closeAction, undefined, lazyModalService); | |
} | |
/** | |
* Add a modal listener with the ability to pass in custom options. | |
* | |
* @param id Unique id for this component. | |
* @param component Component type to instantiate. | |
* @param selectFn Selector selecting a boolean state attribute that determines modal visibility. | |
* @param closeAction Action creator, this action is fired if the modal is closed. | |
* @param options NgbModalOptions to use. If an observable is passed the latest emitted value is used before each modal open. | |
* @param lazyModalService NgbModal instance of the lazy loaded module. | |
*/ | |
addModalListenerEx( | |
id: string, | |
component: Type<any>, | |
selectFn: (state: any) => boolean, | |
closeAction: (reason: string) => Action, | |
options: {} | Observable<{}>, | |
lazyModalService: NgbModal, | |
): Subscription { | |
lazyModalService = lazyModalService || this.modalService; | |
const optionsObservable: Observable<{}> = (options && isObservable(options)) ? options : Observable.of(options); | |
const subscription = this.store.select(selectFn) | |
.withLatestFrom(optionsObservable) | |
.subscribe(([showModal, resolvedOptions]) => { | |
if (showModal) { | |
if (!this.modals[id] || !this.modals[id].componentInstance) { | |
this.modals[id] = lazyModalService.open(component, resolvedOptions); | |
// If modal is closed by clicking outside of it we still need to set the correct state. | |
this.modals[id].result.then(() => { | |
this.modals[id] = null; | |
this.store.dispatch(closeAction('External')); | |
}, () => { | |
this.modals[id] = null; | |
this.store.dispatch(closeAction('External')); | |
}); | |
} | |
} else { | |
if (this.modals[id] && this.modals[id].componentInstance) { | |
this.modals[id].close(); | |
this.modals[id] = null; | |
} | |
} | |
}); | |
return subscription; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@NgModule({...}) | |
export class UserModule { | |
constructor( | |
ngbModal: NgbModal, | |
modalService: ModalService, | |
store: Store<AppState> | |
) { | |
// Disallow closing the login modal if user is accessing a protected route as first page. | |
// (else they would get an empty page due to the auth guard) | |
let loginModalOptions = store.select(x => x.user.loginRequired) | |
.map(required => required ? { | |
backdrop: 'static', | |
keyboard: false | |
} : {}); | |
modalService.addModalListenerEx( | |
'Login', | |
LoginModalComponent, | |
appState => appState.user.loginModalVisible, | |
() => new userActions.CloseLoginModalAction(), | |
loginModalOptions, | |
ngbModal | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment