Angular Service - Class variable instantiated values are lost - angular9

I'm trying to write a service class that listens for page visibility and provides the state change to the subscribed component(s). The service class has an instance variable doc that pretty much holds the current HTML document. I set this doc from the components init method and subscribed in the component to listen for page visibility state change. Even though in the debug window, I see the state change happen, the doc variable is null there and I'm not sure why.
Below is my service class
import { Subject } from "rxjs";
import { Injectable } from '#angular/core';
export enum PageVisibilityState {
Visible,
Hidden
}
#Injectable()
export class PageVisibilityService {
doc: Document;
hiddenString:string = "";
visibilityChange:string="";
pageVisibilityState: PageVisibilityState = PageVisibilityState.Visible;
_pageVisibilityStateSubject: Subject<PageVisibilityState> = new Subject();
constructor() {
this.doc = null;
}
init(doc: Document) {
this.doc = doc;
// Set the name of the "hidden" property and the change event for visibility
if (typeof doc.hidden !== "undefined") {
this.hiddenString = "hidden";
this.visibilityChange = "visibilitychange";
} else if (typeof doc["mozHidden"] !== "undefined") { // Firefox up to v17
this.hiddenString = "mozHidden";
this.visibilityChange = "mozvisibilitychange";
} else if (typeof doc["webkitHidden"] !== "undefined") { // Chrome up to v32, Android up to v4.4, Blackberry up to v10
this.hiddenString = "webkitHidden";
this.visibilityChange = "webkitvisibilitychange";
}
// Warn if the browser doesn't support addEventListener or the Page Visibility API
if (typeof doc.addEventListener === "undefined" || typeof doc[this.hiddenString] === "undefined") {
console.log("This demo requires a modern browser that supports the Page Visibility API.");
} else {
// Handle page visibility change
doc.addEventListener(this.visibilityChange, this.handleVisibilityChange, false);
}
}
private handleVisibilityChange() {
if (typeof this.doc.hidden !== "undefined") { //***this.doc <-- is null here***
this.hiddenString = "hidden";
this.visibilityChange = "visibilitychange";
} else if (typeof this.doc["mozHidden"] !== "undefined") { // Firefox up to v17
this.hiddenString = "mozHidden";
this.visibilityChange = "mozvisibilitychange";
} else if (typeof this.doc["webkitHidden"] !== "undefined") { // Chrome up to v32, Android up to v4.4, Blackberry up to v10
this.hiddenString = "webkitHidden";
this.visibilityChange = "webkitvisibilitychange";
}
if (this.doc[this.hiddenString]) {
this.pageVisibilityStateSubject.next(PageVisibilityState.Hidden);//**all instances are null**
} else {
this.pageVisibilityStateSubject.next(PageVisibilityState.Visible);
}
}
get pageVisibilityStateSubject() : Subject<PageVisibilityState> {
return this._pageVisibilityStateSubject;
}
set pageVisibilityStateSubject(src: Subject<PageVisibilityState>) {
this._pageVisibilityStateSubject = src;
}
public getVisibilityState(): Subject<PageVisibilityState> {
return this.pageVisibilityStateSubject;
}
dispose(): void {
//Called once, before the instance is destroyed.
//Add 'implements OnDestroy' to the class.
this.doc = null;
}
}
Here is my app.module.ts
//some imports removed for easy readability
import { PageVisibilityService } from './shared/services/page.visibility.service';
#NgModule({
imports: [//imports removed for easy readability ],
declarations: [//removed for easy readability ],
providers: [
//some removed for easy readability
PageVisibilityService
],
bootstrap: [AppComponent]
})
export class AppModule { }
Here is my component
import { PageVisibilityService, PageVisibilityState } from '../../../shared/services/page.visibility.service';
#Component({
selector: 'app-team-consult-action-teamConsult',
templateUrl: './team-consult-action.component.html',
styleUrls: ['./team-consult-action.component.scss'],
animations: [routerTransition()]
})
export class TeamConsultActionComponent implements OnInit {
pageVisibilityStateSubject: Subject<PageVisibilityState>;
constructor(
public router: Router,
private route: ActivatedRoute,
private pageVisibilityService: PageVisibilityService,
private commonService: CommonService) {
this.pageVisibilityService.init(document);
this.pageVisibilityStateSubject = this.pageVisibilityService.pageVisibilityStateSubject;
this.pageVisibilityStateSubject.subscribe((pageVisible: PageVisibilityState) => {
console.log(`Current Page State is ${pageVisible === PageVisibilityState.Visible ? "Visible" : "Hidden"}`);
});
}
In the console log, I'm getting the following error:
TypeError: Cannot read properties of undefined (reading 'next')
at HTMLDocument.handleVisibilityChange (page.visibility.service.ts:57:45)
at ZoneDelegate.invokeTask (zone-evergreen.js:399:1)
at Object.onInvokeTask (core.js:41442:1)
at ZoneDelegate.invokeTask (zone-evergreen.js:398:1)
at Zone.runTask (zone-evergreen.js:167:1)
at ZoneTask.invokeTask [as invoke] (zone-evergreen.js:480:1)
at invokeTask (zone-evergreen.js:1621:1)
at HTMLDocument.globalZoneAwareCallback (zone-evergreen.js:1647:1)
Can you please assist with what is causing my instances to lose their initializations?
Update
I understood the cause of null issue why when debugging, the console shows null. The reason is that the PageVisibilityChange which the windows invokes is handleVisibilityChange method (in my class). But window doesn't have reference to our initialized object and it therefore is not in our instance that this happens. So, a possible solution I think could be to store the instance in sessionStorage show how and use that instance reference back to push the state values. Anyone else have faced this issue and were able to resolve this? or let me know if there is a common class using which I could I achieve this.
Thanks,
Hemant.

Finally, my issue is solved by making the methods and members static because with one tab, there is going to be one browser so safe to go with static option and below is the updated code just in case anyone wants to make use of it.
import { Subject } from "rxjs";
import { Injectable } from '#angular/core';
export enum PageVisibilityState {
Hidden = 0,
Visible = 1
}
#Injectable()
export class PageVisibilityService {
static hiddenString:string = "";
static pageVisibilityState: PageVisibilityState = PageVisibilityState.Visible;
static _PageVisibilityStateSubject: Subject<PageVisibilityState> = new Subject();
constructor() {
var visibilityChange:string="";
// Set the name of the "hidden" property and the change event for visibility
if (typeof document.hidden !== "undefined") {
PageVisibilityService.hiddenString = "hidden";
visibilityChange = "visibilitychange";
} else if (typeof document["mozHidden"] !== "undefined") { // Firefox up to v17
PageVisibilityService.hiddenString = "mozHidden";
visibilityChange = "mozvisibilitychange";
} else if (typeof document["webkitHidden"] !== "undefined") { // Chrome up to v32, Android up to v4.4, Blackberry up to v10
PageVisibilityService.hiddenString = "webkitHidden";
visibilityChange = "webkitvisibilitychange";
}
// Warn if the browser doesn't support addEventListener or the Page Visibility API
if (typeof document.addEventListener === "undefined" || typeof document[PageVisibilityService.hiddenString] === "undefined") {
console.log("This demo requires a modern browser that supports the Page Visibility API.");
} else {
// Handle page visibility change
document.addEventListener(visibilityChange, PageVisibilityService.handleVisibilityChange, false);
}
}
static handleVisibilityChange() {
if (document[PageVisibilityService.hiddenString]) {
PageVisibilityService._PageVisibilityStateSubject.next(PageVisibilityState.Hidden);
} else {
PageVisibilityService._PageVisibilityStateSubject.next(PageVisibilityState.Visible);
}
}
get PageVisibilityStateSubject() : Subject<PageVisibilityState> {
return PageVisibilityService._PageVisibilityStateSubject;
}
set PageVisibilityStateSubject(src: Subject<PageVisibilityState>) {
PageVisibilityService._PageVisibilityStateSubject = src;
}
}

Related

Syncfusion NumericTextbox disable select

I am using an Angular NumericTextbox from Syncfusion in my application. We ran in the issue that when you click on the input it will automaticly select it. Is there a way to disable it?
Issue:
https://gyazo.com/a72bd4aaf4ebda7a98256d31e3959a48
Docs:
https://ej2.syncfusion.com/angular/documentation/numerictextbox/getting-started/
HTML:
<ejs-numerictextbox
[floatLabelType]="floatLabelType"
[enabled]="enabled"
[min]="min"
[max]="max"
[placeholder]="caption"
[format]="format"
[ngClass]="{
'e-success': (control?.dirty || control?.touched) && !control?.invalid,
'e-error': (control?.dirty || control?.touched) && control?.invalid,
'hum-show-required': !this.hideRequired,
'hum-required': isRequired()
}"
[currency]="currency"
(change)="updateControlValue($event)"
(blur)="handleBlur($event)"
></ejs-numerictextbox>
TS
export class FormNumberComponent extends FormBaseComponent {
#ViewChild(NumericTextBoxComponent, { static: true }) valueAccessor: ControlValueAccessor;
#Input() format: string = 'n0';
#Input() min = 0;
#Input() max: number;
#Input() currency = 'EUR';
private busy: boolean;
constructor(injector: Injector, stateService: StateService) {
super(injector);
this.initLogging(false, 'FormNumberComponent');
this.currency = stateService.getCurrency();
}
updateControlValue(event: any): void {
console.log(event);
setTimeout(() => {
// todo - hacky way to fix the issue (integration of ejs with form needs to be refactored)
const formControl: NumericTextBoxComponent = this.valueAccessor as NumericTextBoxComponent;
if (!isObjectEmpty(formControl) && !formControl.isDestroyed) {
this.busy = true;
formControl.focusIn();
formControl.focusOut();
this.busy = false;
}
});
}
handleBlur(e) {
if (!this.busy) {
super.handleBlur(e);
}
}
}
Your requirement to disable the auto select functionality of the Numeric textbox inputs can be achieved by using the click event. please check the code below,
Code snippet
<ejs-numerictextbox
value="10"
(click)="OnClick($event)">
</ejs-numerictextbox>
OnClick(args): void {
var position = args.srcElement.selectionEnd;
args.srcElement.selectionStart = args.srcElement.selectionEnd = position;
}
Sample: https://stackblitz.com/edit/angular-vgqmzs-i93zpr?file=app.component.ts

how to update changes on view in Angular

I have a charts that have a few options for user and I already implemented everything but not sure why I don't see any immediate changes when I click Delete Chart, Publish Chart or Unpublished Chart button. I can only see the result only after I refresh the browser.
I'm new to Angular so I'm wonder how to remove the selected chart immediately or make it disappear when delete button is click and also the same for publish and unpublish chart without having to refresh the browser. any help or suggestion will be really appreciated
#Input() chart: Chart;
data: ChartData;
chartData: ChartData;
hasError: boolean = false;
maxisChartConfig: ChartConfig;
hasChart: boolean = false;
#Input() editMode: boolean;
isTextChart: boolean = false;
constructor(private chartService: ChartService, private router: Router, private dialog: MatDialog) { }
isGrid: boolean = false;
#Input() wsType?: WorkspaceType;
isPublicWs: boolean = false;
ngOnInit(): void {
if(this.wsType) {
if(this.wsType == WorkspaceType.public) {
this.isPublicWs = true;
}
}
this.generateChartConfig();
if(this.chart.chartType == ChartType.text){
this.isTextChart = true;
}
else if(this.chart.chartType == ChartType.grid){
this.isGrid = true;
}
if (this.chart.data) {
if(!this.isTextChart){
this.hasChart = true;
}
this.chartData = this.chart.data;
}
}
deleteChart() {
this.chartService.deleteChart(this.chart.guid).subscribe((deleted) => {
console.log(deleted);
});
}
publishChart() {
this.chartService.setChartPublished(this.chart.guid, !this.chart.isPublished).subscribe((published) => {
console.log(published);
});
}
<button mat-menu-item (click) = "deleteChart()" *ngIf = "chart.hasAccess && chart.canEdit && !chart.isPublished">Delete Chart</button>
<button mat-menu-item (click) = "publishChart()" *ngIf = "chart.canEdit && chart.hasAccess && !chart.isPublished && isPublicWs">Publish Chart</button>
<button mat-menu-item (click) = "publishChart()" *ngIf = "chart.canEdit && chart.hasAccess && chart.isPublished && isPublicWs">Unpublish Chart</button>
The will not run but I uploaded the full code for this component here https://stackblitz.com/edit/angular-ivy-bepxss . Thanks
After each function you can call oninit to reconstruct the charts after changes like this -
deleteChart() {
this.chartService.deleteChart(this.chart.guid).subscribe((deleted) => {
console.log(deleted);
this.ngOnInit(); // Add this line
});
}
///This is how i have refreshed variables in my case -
saveWTPModel(){
if(some condition){
//Perform Save logic
var headers = new HttpHeaders();
headers.append('Content-Type', 'application/json');
const httpOptions = { headers: headers };
this.httpClient.post(environment.api_url + "/User/SavePriority",
this.finalWTPArray, httpOptions)
.subscribe(
response => {
if(response){
this.alertService.success('Priorities Saved.');
//Once i have saved everything I am again calling the api to get the updated data from backend. This function again is called to get the updated Priorities.
this.getWorkTypesPriority(this.loggedinUserId);
}
else{
this.alertService.warning('Problem Occurred.');
}
});
}

Angular directive *ngIf not working as intended

I have this block of html in my template to show or hide the div.
<div *ngIf="csvVisible">
<p>Paragraph works</p>
</div>
This is my component.
export class SettingsComponent implements OnInit {
csvVisible: boolean = false;
private dataSource: string[];
#ViewChild(MatTable, { static: true }) table: MatTable<any>;
constructor(private dialog: MatDialog, private templateParserService: TemplateParserService) { }
ngOnInit() {
this.templateParserService.subscribe({
next(result: string[]) {
if (result !== null) {
this.dataSource = result;
if (this.dataSource && this.dataSource.length) {
this.csvVisible = true;
} else {
this.csvVisible = false;
}
}
},
error(error: Error) {
console.log(error.message);
}
});
}
Eventhough the DIV is hidden at start, it doesnt automatically show / hide on the csvVisible value change. Value of csvVisible is properly set when the observer emits data. [hidden]="csvVisible" isn't working either.
Edit :
Subscriber registration on the service is done by the following code.
private subject = new Subject<string[]>();
public subscribe(observer: any): Subscription {
return this.subject.subscribe(observer);
}
Since you are using Object inside subscribe, this points to current subscribe object, Instead of using subscribe({next:()}) try using this way
component.ts
this.templateParserService.subscribe((result: string[])=>{
if (result !== null) {
this.dataSource = result;
if (this.dataSource && this.dataSource.length) {
this.csvVisible = true;
} else {
this.csvVisible = false;
}
}
},(error: Error)=>{
console.log(error.message);
});

angular 2 handle click on link inside iframe

Is there a way to catch a click on a link inside an iframe?
<iframe *ngIf="currentFrameUrl" id="contentFrame" [src]="currentFrameUrl | safe"></iframe>
My iframe component contains just a simple code which subscribes to observable variables.
export class ContentFrameComponent implements OnInit {
currentFrameUrl: string;
currentMenuState: boolean;
#ViewChild('contentFrame') iframe: ElementRef;
constructor(private frameService: ContentFrameService) {
this.currentMenuState = true;
}
ngOnInit(): void {
this.frameService.providerCurrentUrl$.subscribe(data => this.currentFrameUrl = data);
this.frameService.providerMenuState$.subscribe(data => this.currentMenuState = data);
}
ngAfterViewInit() {
let doc = this.iframe.nativeElement.contentDocument ||this.iframe.nativeElement.contentWindow;
if (typeof doc.addEventListener !== undefined) {
console.log("inside if - addEventListener") // Is shown
doc.addEventListener("click", this.iframeClickHandler, false)
} else if (typeof doc.attachEvent !== undefined) {
console.log("inside if - attachEvent ") // Does not show
doc.attachEvent("onclick", this.iframeClickHandler)
}
}
iframeClickHandler(): void {
console.log("Click test") // Does not show
}
}
My goal is to to catch click event on iframe link, stop its propagation and set the url of the iframe link using either router.navigate or location.go/location.replaceState. Not sure yet which option is better.
I have no control over the pages loaded inside the iframe. I only know, that the links should contains such url that can be used with my dynamic routes.
use #HostListener('window:blur', ['$event']) decorator, method attached with this decorator trigger on IFrame click.
#HostListener('window:blur', ['$event'])
onWindowBlur(event: any): void {
console.log('iframe clicked');
}
Note: Also this will work for cross-domain operations.
<iframe *ngIf="currentFrameUrl" id="contentFrame" [src]="currentFrameUrl | safe" #iframe></iframe>
And
export class ContentFrameComponent implements OnInit {
currentFrameUrl: string;
currentMenuState: boolean;
#ViewChild('iframe') iframe: ElementRef;
constructor(private frameService: ContentFrameService) {
this.currentMenuState = true;
}
ngOnInit(): void {
this.frameService.providerCurrentUrl$.subscribe(data => this.currentFrameUrl = data);
this.frameService.providerMenuState$.subscribe(data => this.currentMenuState = data);
}
ngAfterViewInit() {
let doc = this.iframe.nativeElement.contentDocument || this.iframe.nativeElement.contentWindow;
if (typeof doc.addEventListener !== undefined) {
doc.addEventListener("click", this.iframeClickHandler, false)
} else if (typeof doc.attachEvent !== undefined) {
doc.attachEvent("onclick", this.iframeClickHandler);
}
}
iframeClickHandler() {
alert("Iframe clicked");
}
}
Note: this might not work for cross-domain operations.
Why you can not use simple onclick?
Try this code:
<iframe *ngIf="currentFrameUrl" id="contentFrame" [src]="currentFrameUrl | safe" (onclick)="clicked()"></iframe>
clicked

Detect click outside Angular component

How can I detect clicks outside a component in Angular?
import { Component, ElementRef, HostListener, Input } from '#angular/core';
#Component({
selector: 'selector',
template: `
<div>
{{text}}
</div>
`
})
export class AnotherComponent {
public text: String;
#HostListener('document:click', ['$event'])
clickout(event) {
if(this.eRef.nativeElement.contains(event.target)) {
this.text = "clicked inside";
} else {
this.text = "clicked outside";
}
}
constructor(private eRef: ElementRef) {
this.text = 'no clicks yet';
}
}
A working example - click here
An alternative to AMagyar's answer. This version works when you click on element that gets removed from the DOM with an ngIf.
http://plnkr.co/edit/4mrn4GjM95uvSbQtxrAS?p=preview
private wasInside = false;
#HostListener('click')
clickInside() {
this.text = "clicked inside";
this.wasInside = true;
}
#HostListener('document:click')
clickout() {
if (!this.wasInside) {
this.text = "clicked outside";
}
this.wasInside = false;
}
Binding to a document click through #Hostlistener is costly. It can and will have a visible performance impact if you overuse it (for example, when building a custom dropdown component and you have multiple instances created in a form).
I suggest adding a #Hostlistener() to the document click event only once inside your main app component. The event should push the value of the clicked target element inside a public subject stored in a global utility service.
#Component({
selector: 'app-root',
template: '<router-outlet></router-outlet>'
})
export class AppComponent {
constructor(private utilitiesService: UtilitiesService) {}
#HostListener('document:click', ['$event'])
documentClick(event: any): void {
this.utilitiesService.documentClickedTarget.next(event.target)
}
}
#Injectable({ providedIn: 'root' })
export class UtilitiesService {
documentClickedTarget: Subject<HTMLElement> = new Subject<HTMLElement>()
}
Whoever is interested for the clicked target element should subscribe to the public subject of our utilities service and unsubscribe when the component is destroyed.
export class AnotherComponent implements OnInit {
#ViewChild('somePopup', { read: ElementRef, static: false }) somePopup: ElementRef
constructor(private utilitiesService: UtilitiesService) { }
ngOnInit() {
this.utilitiesService.documentClickedTarget
.subscribe(target => this.documentClickListener(target))
}
documentClickListener(target: any): void {
if (this.somePopup.nativeElement.contains(target))
// Clicked inside
else
// Clicked outside
}
Improving J. Frankenstein's answer:
#HostListener('click')
clickInside($event) {
this.text = "clicked inside";
$event.stopPropagation();
}
#HostListener('document:click')
clickOutside() {
this.text = "clicked outside";
}
The previous answers are correct, but what if you are doing a heavy process after losing the focus from the relevant component? For that, I came with a solution with two flags where the focus out event process will only take place when losing the focus from relevant component only.
isFocusInsideComponent = false;
isComponentClicked = false;
#HostListener('click')
clickInside() {
this.isFocusInsideComponent = true;
this.isComponentClicked = true;
}
#HostListener('document:click')
clickout() {
if (!this.isFocusInsideComponent && this.isComponentClicked) {
// Do the heavy processing
this.isComponentClicked = false;
}
this.isFocusInsideComponent = false;
}
ginalx's answer should be set as the default one imo: this method allows for many optimizations.
The problem
Say that we have a list of items and on every item we want to include a menu that needs to be toggled. We include a toggle on a button that listens for a click event on itself (click)="toggle()", but we also want to toggle the menu whenever the user clicks outside of it. If the list of items grows and we attach a #HostListener('document:click') on every menu, then every menu loaded within the item will start listening for the click on the entire document, even when the menu is toggled off. Besides the obvious performance issues, this is unnecessary.
You can, for example, subscribe whenever the popup gets toggled via a click and start listening for "outside clicks" only then.
isActive: boolean = false;
// to prevent memory leaks and improve efficiency, the menu
// gets loaded only when the toggle gets clicked
private _toggleMenuSubject$: BehaviorSubject<boolean>;
private _toggleMenu$: Observable<boolean>;
private _toggleMenuSub: Subscription;
private _clickSub: Subscription = null;
constructor(
...
private _utilitiesService: UtilitiesService,
private _elementRef: ElementRef,
){
...
this._toggleMenuSubject$ = new BehaviorSubject(false);
this._toggleMenu$ = this._toggleMenuSubject$.asObservable();
}
ngOnInit() {
this._toggleMenuSub = this._toggleMenu$.pipe(
tap(isActive => {
logger.debug('Label Menu is active', isActive)
this.isActive = isActive;
// subscribe to the click event only if the menu is Active
// otherwise unsubscribe and save memory
if(isActive === true){
this._clickSub = this._utilitiesService.documentClickedTarget
.subscribe(target => this._documentClickListener(target));
}else if(isActive === false && this._clickSub !== null){
this._clickSub.unsubscribe();
}
}),
// other observable logic
...
).subscribe();
}
toggle() {
this._toggleMenuSubject$.next(!this.isActive);
}
private _documentClickListener(targetElement: HTMLElement): void {
const clickedInside = this._elementRef.nativeElement.contains(targetElement);
if (!clickedInside) {
this._toggleMenuSubject$.next(false);
}
}
ngOnDestroy(){
this._toggleMenuSub.unsubscribe();
}
And, in *.component.html:
<button (click)="toggle()">Toggle the menu</button>
Alternative to MVP, you only need to watch for Event
#HostListener('focusout', ['$event'])
protected onFocusOut(event: FocusEvent): void {
console.log(
'click away from component? :',
event.currentTarget && event.relatedTarget
);
}
Solution
Get all parents
var paths = event['path'] as Array<any>;
Checks if any parent is the component
var inComponent = false;
paths.forEach(path => {
if (path.tagName != undefined) {
var tagName = path.tagName.toString().toLowerCase();
if (tagName == 'app-component')
inComponent = true;
}
});
If you have the component as parent then click inside the component
if (inComponent) {
console.log('clicked inside');
}else{
console.log('clicked outside');
}
Complete method
#HostListener('document:click', ['$event'])
clickout(event: PointerEvent) {
var paths = event['path'] as Array<any>;
var inComponent = false;
paths.forEach(path => {
if (path.tagName != undefined) {
var tagName = path.tagName.toString().toLowerCase();
if (tagName == 'app-component')
inComponent = true;
}
});
if (inComponent) {
console.log('clicked inside');
}else{
console.log('clicked outside');
}
}
You can use the clickOutside() method from the ng-click-outside package; it offers a directive "for handling click events outside an element".
NB: This package is currently deprecated. See https://github.com/arkon/ng-sidebar/issues/229 for more info.
Another possible solution using event.stopPropagation():
define a click listener on the top most parent component which clears the click-inside variable
define a click listener on the child component which first calls the event.stopPropagation() and then sets the click-inside variable
You can call an event function like (focusout) or (blur); then you would put in your code:
<div tabindex=0 (blur)="outsideClick()">raw data </div>
outsideClick() {
alert('put your condition here');
}
nice and tidy with rxjs.
i used this for aggrid custom cell editor to detect clicks inside my custom cell editor.
private clickSubscription: Subscription | undefined;
public ngOnInit(): void {
this.clickSubscription = fromEvent(document, "click").subscribe(event => {
console.log("event: ", event.target);
if (!this.eRef.nativeElement.contains(event.target)) {
// ... click outside
} else {
// ... click inside
});
public ngOnDestroy(): void {
console.log("ON DESTROY");
this.clickSubscription?.unsubscribe();
}