UPDATE:
I've refactored based on @Blind Despair's answer below. It now uses the Overlay Module from Angular CDK. Here's the code for the Guard:
@Injectable({
providedIn: 'root',
})
export class PendingChangesGuard
implements CanDeactivate<ComponentCanDeactivate> {
content = 'A simple string content modal overlay';
constructor(private overlayService: OverlayService) {}
canDeactivate(
component: ComponentCanDeactivate
): boolean | Observable<boolean> {
return component.canDeactivate()
? true
: this.openConfirmDialog(this.content);
}
openConfirmDialog(content: TemplateRef<any> | ComponentType<any> |
string) {
const ref = this.overlayService.open(this.content, null);
return ref.afterClosed$.subscribe((res) => {});
}
}
The problem is this line is returning a subscription and my guard has to return either a boolean or Observable:
return ref.afterClosed$.subscribe((res) => {});
Here is the OverlayService class:
@Injectable({
providedIn: 'root',
})
export class OverlayService {
constructor(private overlay: Overlay, private injector: Injector) {}
open<R = any, T = any>(
content: string | TemplateRef<any> | Type<any>,
data: T
): OverlayRef<R> {
const configs = new OverlayConfig({
hasBackdrop: true,
panelClass: ['modal', 'is-active'],
backdropClass: 'modal-background',
});
const overlayRef = this.overlay.create(configs);
const myOverlayRef = new OverlayRef<R, T>(overlayRef, content,
data);
const injector = this.createInjector(myOverlayRef, this.injector);
overlayRef.attach(new ComponentPortal(OverlayComponent, null,
injector));
return myOverlayRef;
}
createInjector(ref: OverlayRef, inj: Injector) {
const injectorTokens = new WeakMap([[RaOverlayRef, ref]]);
return new PortalInjector(inj, injectorTokens);
}
}
And here's the Overlay class:
export interface OverlayCloseEvent<R> {
type: 'backdropClick' | 'close';
data: R;
}
// R = Response Data Type, T = Data passed to Modal Type
export class OverlayRef<R = any, T = any> {
afterClosed$ = new Subject<OverlayCloseEvent<R>>();
constructor(
public overlay: OverlayRef,
public content: string | TemplateRef<any> | Type<any>,
public data: T // pass data to modal i.e. FormData
) {
overlay.backdropClick().subscribe(() =>
this._close('backdropClick', null));
}
close(data?: R) {
this._close('close', data);
}
private _close(type: 'backdropClick' | 'close', data: R) {
this.overlay.dispose();
this.afterClosed$.next({
type,
data,
});
this.afterClosed$.complete();
}
}
Any tips for how to refactor the openConfirmDialog() to return what I need?