'How to close a mat custom overlay on clicking outside when there is no backdrop?
I have created a dropdown panel using custom overlay from material, I need that if I set the property hasBackdrop: false, overlay should close if I click outside of dropdown panel just like mat autocomplete. I have written _getOutsideClickStream() to get outside click events. But panel gets open on click of trigger lets say a button and gets detached at that time only.
HTML
<button #trigger>
settings
</button>
<my-dropdown-panel #dropdownPanel [origin]="trigger">some content</my-dropdown-panel>
@Component({
selector: 'my-dropdown-panel',
templateUrl: './dropdown-panel.component.html',
encapsulation: ViewEncapsulation.None
})
export class DropdownPanelComponent implements TriggerTarget, OnChanges {
@Input() origin: CdkOverlayOrigin;
@Input() opened: boolean;
@Input() hasBackdrop = true;
@Output() openedChange: EventEmitter<boolean> = new EventEmitter<boolean>();
@Input() type = DropdownType.Dropdown;
@Input() xPosition = 'center';
@Input() yPosition = 'bottom';
className = 'my-dropdown-panel-container';
@Input() closeIcon: boolean;
private _overlayRef: OverlayRef;
private _portal: TemplatePortal<any>;
@ViewChild(TemplateRef, {static: false}) content: TemplateRef<any>;
@Output() toggle = new EventEmitter<boolean>();
clickoutHandler: Function;
/** Whether the element is inside of a ShadowRoot component. */
private _isInsideShadowRoot: boolean;
constructor(private _overlay: Overlay,
private _viewContainerRef: ViewContainerRef,
private _dir: Directionality,
private _zone: NgZone,
private _eref: ElementRef,
@Inject(DOCUMENT) private _document: any,
private _element: ElementRef<HTMLInputElement>) {
super();
this._dir.change.subscribe(() => {
// detach overlay to create new overlay with updated value;
if (this._overlayRef) {
this._overlayRef.detach();
this._overlayRef = null;
}
})
}
ngOnChanges(changes: SimpleChanges): void {
if (changes &&
changes.hasBackdrop &&
changes.hasBackdrop.previousValue !== changes.hasBackdrop.currentValue) {
if (this._overlayRef) {
this._overlayRef.detach();
this._overlayRef = null;
}
}
}
open() {
const origin = this._getOrigin();
const overlay = this._getOverlayPosition();
if (!this._overlayRef) {
let positionStrategy: FlexibleConnectedPositionStrategy;
positionStrategy = this._overlay.position()
.flexibleConnectedTo(this.origin.elementRef)
.withPositions([
{ ...origin.main, ...overlay.main },
{...origin.fallback, ...overlay.fallback}
]);
this.onPositionChanged({
connectionPair: {
overlayX: overlay.main.overlayX,
overlayY: overlay.main.overlayY
}
});
positionStrategy.positionChanges.subscribe(change => {
this.onPositionChanged(change);
});
this._overlayRef = this._overlay.create({
hasBackdrop: this.hasBackdrop,
backdropClass: 'my-dropdown-panel-wrapper',
positionStrategy,
direction: this._dir.value
});
if (this._isInsideShadowRoot == null) {
this._isInsideShadowRoot = !!_getShadowRoot(this._element.nativeElement);
}
this._overlayRef.backdropClick().subscribe(() => this.close());
this._getOutsideClickStream().subscribe(()=>{
this.close();
});
this._portal = new TemplatePortal(this.content, this._viewContainerRef);
}
if (!this._overlayRef.hasAttached()) {
this._overlayRef.attach(this._portal);
this.opened = true;
this.toggle.emit(true);
} else {
this.close();
this.toggle.emit(false);
}
};
private _getOutsideClickStream(): Observable<any> {
return merge(
fromEvent(this._document, 'click') as Observable<MouseEvent>,
fromEvent(this._document, 'touchend') as Observable<TouchEvent>)
.pipe(filter(event => {
// If we're in the Shadow DOM, the event target will be the shadow root, so we have to
// fall back to check the first element in the path of the click event.
const clickTarget =
(this._isInsideShadowRoot && event.composedPath ? event.composedPath()[0] :
event.target) as HTMLElement;
return this._overlayRef.hasAttached() && clickTarget !== this._element.nativeElement &&
(!!this._overlayRef && !this._overlayRef.overlayElement.contains(clickTarget));
}));
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|