I've card component, Home Page and Explore page. I have show all the cards in Explore page and user have the ability to favorite a particular card and it'll display in the Homepage.
the problem I have right now is
the favorited cards only appear in Homepage when I refresh the browser, It didn't show anything when I navigate using toolbar.
When I filtered particular favorited cards then delete it, it still appear. for example, I typed "food" in search bar and it appear 5 card, I delete 1 and cancel my search then the delete card still appearing. It only gone after refreshing the browser again.
Can anybody help me fix this bugs?.
Card Component favorite and unfavorited function
toggleFavorite(): void {
if (!this.card.isFavorite) {
this.userService.addUserFavorite(this.card.type, this.card.guid, 0, 0).subscribe((res) => {
if (res) {
this.card.isFavorite = true;
this.removeFromList.emit(this.card.id);
this.refreshCardList.emit(true);
}
});
} else {
this.userService.removeFavorite(this.card.guid, this.card.type).subscribe((res) => {
if (res) {
this.card.isFavorite = false;
this.refreshCardList.emit(true);
this.removeFromList.emit(this.card.id);
}
});
}
Home Component, this is how I display user favorited card in Home page
<div *ngIf="favorites.length > 0">
<app-card-list [cards]="favorites"></app-card-list>
</div>
Home Component ts
favorites: List<Favorite>;
loading: boolean;
getUserFavs$: Subject<void> = new Subject();
searchInput: string;
user: User;
query: string;
inputCtrl: FormControl = new FormControl();
constructor(private userService: UserService, private cdr: ChangeDetectorRef) {}
ngOnInit(): void {
this.loading = true;
this.getUserFavs$
.pipe(
switchMap(() => {
return this.userService.getUser();
}),
)
.subscribe((user: User) => {
this.favorites = user.userFavorites;
this.user = JSON.parse(JSON.stringify(user));
this.loading = false;
this.cdr.detectChanges(); // treid this but still not working
});
this.getUserFavs$.next();
this.filterChanged();
this.cdr.detectChanges();
}
Use ChangeDetectorRef...
constructor(private change: ChangeDetectorRef){
}
After getting response from service and assigning to card variable
Use this.change.detectChanges()
Related
i'm struggling on something that should be pretty easy.
I'm trying to render a spinner, whenever a get call is ongoing, so instead of displaying an empty screen, i can use the spinner.
I thought of using two separate div, controlled by two ngIf, related to the same bool flag. Of course if one is *ngIf="flag", the other one is *ngIf="!flag".
I edit the value, inside the 'subscribe' of the my get call, but unfortunately, the bool (although it changes), does not affect the html (probably because how angular works, and lifecycle of the components).
Do you know how can i do this ?
In my data service component i have a really simple http get to fill my variable 'products : Product[]', and it works.
In my component shop.ts i have
#Component({
selector: 'app-shop',
templateUrl: './shop.component.html',
styleUrls: ['./shop.component.css'],
})
export class ShopComponent {
constructor(public ds: DataService) {}
/* Variables */
products: Product[] = [];
isDataLoaded: boolean = false;
/* With this get call, we get all the products informations, and we save'em
into products */
ngOnInit() {
this.ds.getProducts().subscribe((resp) => {
this.products = resp as Product[];
this.isDataLoaded = true;
}
});
}
In the component html i just have
<div *ngIf="!isDataLoaded">
<mat-spinner></mat-spinner>
</div>
<div *ngIf="isDataLoaded">
Data is loaded
</div>
I do this all the time. Here is the approach I use.
Store the result in a subscription and set it equal to the request like so:
#Component({
selector: 'app-shop',
templateUrl: './shop.component.html',
styleUrls: ['./shop.component.css'],
})
export class ShopComponent {
constructor(public ds: DataService) {}
products: Product[] = [];
isDataLoaded$: Subscription;
ngOnInit() {
this.isDataLoaded$ = this.ds.getProducts().subscribe((resp) =>
this.products = resp as Product[];
);
}
}
Then in your template, check if the subscription exists and is not closed:
<mat-spinner *ngIf="isDataLoaded$ && !isDataLoaded$.closed"></mat-spinner>
<div *ngIf="isDataLoaded$ && isDataLoaded$.closed">
Data is loaded
</div>
Problems with your original approach
If that request fails, your isDataLoaded variable will never update since you don't have an error block. Also, once you set that variable to true, it stays true. What happens if the user fires that request again? You need to also reset it back to false before each request so the spinner shows up.
Here is an improved version of your original code, although I do not recommend going with this approach.
ngOnInit() {
this.isDataLoaded = false;
this.ds.getProducts().subscribe((resp) => {
this.products = resp;
this.isDataLoaded = true;
}, error => {
...
this.isDataLoaded = true;
});
}
Can you show how you implemented the getProduts method?
I tried to replicate your project, like this:
constructor(public ds: DataService) {}
/* Variables */
products: Product[] = [];
isDataLoaded: boolean = false;
/* With this get call, we get all the products informations, and we save'em
into products */
ngOnInit() {
this.ds.getProducts()
.subscribe((resp) => {
this.products = resp;
this.isDataLoaded = true;
});
}
And I implemented the Data Service like this:
constructor(private http: HttpClient) {}
getProducts(): Observable<Product[]> {
return this.http.get<Product[]>('API');
}
And it works. Maybe it works for you too, but the data are loaded so fast that you don't see the spinner.
import { finalize } from 'rxjs';
...
this.ds.getProducts()
.pipe(finalize(() => this.isDataLoaded = true))
.subscribe((resp) => {
this.products = resp as Product[];
});
I use mat-dialog to edit details of my profile page. I'm getting an ExpressionChangedAfterItHasBeenCheckedError when I click the 'Edit age' button and the dialog window pops up.
I decided to extract the styling of all edit dialogs into a single edit.component:
edit.component.html
<div class="navigation-control">
<mat-icon (click)="onCancelButtonClicked()"
class="close-button">close</mat-icon>
</div>
<div class="content-main">
<ng-content select=".content-main"></ng-content>
</div>
<div class="content-bot">
<button mat-raised-button
(click)="onCancelButtonClicked()">Cancel</button>
<button mat-raised-button
(click)="onActionButtonClicked()"
[lnDisableButton]="actionButtonDisabled">{{actionButtonValue}}</button>
</div>
edit.component.ts
#Component({ selector: 'ln-edit', ... })
export class EditComponent {
#Input() actionButtonValue: string;
#Input() actionButtonDisabled: boolean;
#Output() cancelButtonClicked = new EventEmitter<void>();
#Output() actionButtonClicked = new EventEmitter<void>();
onCancelButtonClicked() {
this.cancelButtonClicked.emit();
}
onActionButtonClicked() {
this.actionButtonClicked.emit();
}
}
To avoid the ExpressionChangedAfterItHasBeenCheckedError when trying to disable buttons and controls, I used this snippet. But that didn't solve this issue.
disable-button.directive.ts
#Directive({ selector: '[lnDisableButton]' })
export class DisableButtonDirective {
#Input('lnDisableButton') isDisabled = false;
#HostBinding('attr.disabled')
get disabled() { return this.isDisabled; }
}
The following is the contents of a mat-dialog window. This gets instantiated when I click the 'Edit age' button. When I remove the [actionButtonDisabled]="actionButtonDisabled", the error goes away, but obivously I need that line to make the functionality disable the button.
age-edit.component.html
<ln-edit [actionButtonValue]="actionButtonValue"
[actionButtonDisabled]="actionButtonDisabled"
(cancelButtonClicked)="onCancelButtonClicked()"
(actionButtonClicked)="onActionButtonClicked()">
<form [formGroup]="ageForm"
class="content-main">
<ln-datepicker formControlName="birthday"
[appearance]="'standard'"
[label]="'Birthday'"
class="form-field">
</ln-datepicker>
</form>
</ln-edit>
I handle the disabling/enabling the button in the 'ts' part of the mat-dialog popup.
age-edit.component.ts
#Component({ selector: 'ln-age-edit', ... })
export class AgeEditComponent implements OnInit, OnDestroy {
ageForm: FormGroup;
private initialFormValue: any;
actionButtonDisabled = true;
private unsubscribe = new Subject<void>();
constructor(
private editPhotoDialogRef: MatDialogRef<AgeEditComponent>,
private fb: FormBuilder,
#Inject(MAT_DIALOG_DATA) public dialogData: Date) { }
ngOnInit() {
this.initializeAgeForm();
this.loadDataToAgeForm(this.dialogData);
this.trackFormDistinct();
}
private initializeAgeForm(): void {
this.ageForm = this.fb.group({
birthday: null,
});
}
loadDataToAgeForm(birthday: Date | null): void {
if (!birthday) { return; }
this.ageForm.setValue({ birthday });
this.initialFormValue = this.ageForm.value;
}
get birthdayAC() { return this.ageForm.get('birthday') as AbstractControl; }
get actionButtonValue(): string {
return this.birthdayAC.value ? 'Update age' : 'Add age';
}
onCancelButtonClicked(): void {
this.editPhotoDialogRef.close();
}
onActionButtonClicked(): void {
this.editPhotoDialogRef.close({ ... });
}
trackFormDistinct(): void {
this.ageForm.valueChanges.pipe(
distinctUntilChanged(), // TODO: needed?
takeUntil(this.unsubscribe)
).subscribe(val => {
(this.formValueNotDistinct(this.ageForm.value, this.initialFormValue)
|| this.birthdayAC.value === null)
? this.actionButtonDisabled = true
: this.actionButtonDisabled = false;
});
}
ngOnDestroy() { ... }
}
I suspect this has something to do with content projection, but I'm not sure.
(...or perhaps with my custom 'ln-datepicker'?)
Any ideas?
Thanks.
From what I can tell, the problem resides in trackFormDistinct() method:
trackFormDistinct(): void {
this.ageForm.valueChanges.pipe(
distinctUntilChanged(), // TODO: needed?
takeUntil(this.unsubscribe)
).subscribe(val => {
(this.formValueNotDistinct(this.ageForm.value, this.initialFormValue)
|| this.birthdayAC.value === null)
? this.actionButtonDisabled = true
: this.actionButtonDisabled = false;
});
}
Looks like because of this.ageForm.valueChanges, will have different values in the 2 change detection cycles. I think this.ageForm.valueChanges emits due to <ln-datepicker>.
In a tree of form controls, if one node calls setValue, all its ancestors will have to be updated. I've written more about how Angular Forms work in this article.
I'm thinking of 2 alternatives:
skip the first emission of ageForm since it indicates the initialization of the form control tree, so this is irrelevant to the logic inside subscribe's callback.
this.ageForm.valueChanges.pipe(
skip(1),
distinctUntilChanged(), // TODO: needed?
takeUntil(this.unsubscribe)
).subscribe(/* .... */)
initialize actionButtonDisabled with false, since the error complains that it switched from true to false
actionButtonDisabled = false;
I have a button that turns the pages. I need to remain in this component, change the ID in the address bar and thus I will change the information on the site. Data coming to me, look like this:
{
"users": [
{
"id": "3135",
"name": "Lulu"
},
{
"id": "8173",
"name": "Lala"
},
{
"id": "5783",
"name": "Lolo"
}
]
I am outputting detailed information about the user by getting an id. My navigation has looks like:
{ path: 'list', component: ListComponent },
{ path: '', redirectTo: '/list ', pathMatch: 'full' },
{ path: 'details/:id', component: DetailComponent }
I have a button, by pressing which the transition from '/details/Id' to 'details/nextId'. But this button does not work. This is a request for the next ID in the service. (I get the data from Database API)
private users: User[] = new Array<User>();
private locator = (u: User, id: number) => u.id === id;
getNextId(id: number): number {
let index = this.users.findIndex(u => this.locator(u, id));
if (index > -1) {
return this.users[this.users.length > index + 2
? index + 1 : 0].id;
} else {
return id || 0;
}}
HTML:
<button class="next-user" [routerLink]="['details', userService.getNextId(user.id)]" routerLinkActive="active">
Next User
</button>
DetailsListComponent:
private userService: UserService,
private activeRoute: ActivatedRoute,
private location: Location,
private router: Router) {
activeRoute.params.subscribe(params => {
this.detail = params['details'] === 'details';
let id = params['id'];
if (id !== null) {
Object.assign(this.user, userService.getNextId(id));
}
});
}
detail: boolean = true;
I searched the Internet and here the answer to this question, but found only suitable for php. Please, help me. What am I doing wrong? I just recently began to study angular 7 and do not always understand what to do.
I do not know how to explain it correctly, how to explain my problem. But I will try, I have a page that displays a list of my users. Clicking on the user opens a new page with more detailed information about it. In order not to go back to the page with a list of users and not to select the next user, a page with detailed information about the user assumes the presence of the Next button. When I click on this button, the array is iterated, it is determined by which ID the page is currently open and which ID is the next in this array, after which the page goes to the next ID. That is, the same page is opened, but the information on it is about another user who is following the list after the previous user.
But my button is currently not working and I do not understand why.
I didn't understand what issue you are facing exactly but here is an overview of how you can route to next User id by clicking on button.
// in Component.ts file
// import Router from #angular/router
import { Router } from '#angular/router';
constructor(private _router: Router){}
// to get the next user id
getNextId(id: number): number {
// write your logic to get the next id here
}}
//navigate to user id
getNextUser(id:number){
let nextId = this.getNextId(id); // holds next id
this._router.navigate([`/register-step2/${nextId}`]);
}
<!-- In Component.html -->
<div class="userDetails">
<p> {{user.name}} </p>
<p> {{user.age}} </p>
<p> {{user.occuption}} </p>
</div>
<button class="next-user" (click)="getNextUser(user.id)">
Next Movie
</button>
I assumed that for each userid service gets called and the result of the service is stored on user variable. I Hope it helps .
=== EDIT (After the question was clarified) ===
Now I understand your use case. Here is how I would go about it.
Few things to note so that the example is clear:
list route was renamed to users (makes more sense to me)
Brand new UsersComponent is created to handle all the users-related routes
DetailComponent -> UserDetailsComponent
ListComponent -> UsersListComponent
Those are just my coding conventions so that things are clear. You can leave your naming style.
In your root component, you will have your root <router-outlet>. And you will also have another <router-outlet> in UsersComponent.
In your router settings you would have:
{ path: 'users', component: UsersComponent, children: [
{ path: '', component: UsersListComponent }
{ path: ':id', component: UserDetailsComponent }
] },
{ path: '', redirectTo: '/users ', pathMatch: 'full' }
Notice, I added a new component UsersComponent which will be a parent for both UsersListComponent and UserDetailsComponent. It will hold a <router-outlet> that will hold either UsersListComponent or UserDetailsComponent depending on the child route that is active.
For /users it will render UsersListComponent.
For /users/:id it will render UserDetailsComponent.
So, UsersListComponent will have something like the following template:
<div *ngFor="let user of users" class="user-list-item" >
<p>user.name</p>
<button class="open-user-details"
[routerLink]="['/users', user.id]">
Navigate To Details
</button>
</div>
In UserDetailsComponent you will have something like the following:
<div class="userDetails">
<p> {{user.id}} </p>
<p> {{user.name}} </p>
<p> {{user.age}} </p>
<p> {{user.occuption}} </p>
</div>
<button class="next-user"
[routerLink]="['/users', getNextUserId()]"
routerLinkActive="active">
Next User
</button>
And in .ts file for UserDetailsComponent you would have:
user: User; // complete user details object. ID included
constructor(private userService: UserService,
private activeRoute: ActivatedRoute)
// gets the next user id based on the current user.id
public getNextUserId(): number {
return this.userService.getNextId(this.user.id)
}
public ngOnInit(): void {
this.activeRoute.params.pipe(switchMap((params) => {
return this.userService.getUserById(params.id);
})).subscribe(user => {
this.user = user;
});
}
I assume your UserService has separate methods:
getNextId(curId: number): number
getUserById(userId: number): Observable<User>
I'm running in the same problem. I try to display the sex of the user inside my navbar component. I call for my service to get me the user object and then I try to set my 'gender' for use in HTML. Problem is I need to refresh the page in order to display the gender.. Any help please? :)
export class NavbarComponent implements OnInit {
title = 'navbar';
userIsLoggedIn: boolean;
user: User;
currentUser: Parent;
gender: string;
constructor(private authenticationService: AuthenticationService, private router: Router, private parentService: ParentService) {
authenticationService.userIsloggedIn.subscribe(isLoggedIn => {
this.userIsLoggedIn = isLoggedIn;
this.user = authenticationService.getUser();
});
}
ngOnInit(): void {
this.user = this.authenticationService.getUser();
this.userIsLoggedIn = this.user != undefined;
this.getParentFromUserEmail(this.user.email);
this.getSex();
}
private getParentFromUserEmail(email: string) {
this.parentService.getByEmail(email).map(
(response) => this.currentUser = response).subscribe(data => {
this.gender = data.type;
});
}
getSex() {
return this.gender;
}
}
HTML CODE
<div class="sidebar-account-content">
<h3>{{user?.firstname}} {{user?.lastname}}</h3>
<p *ngIf="getSex()">Test</p>
<p *ngIf="gender === 'F'">Father</p>
<p *ngIf="gender === 'M'">Mother</p>
</div>
I suppose that your "getUser" is a asynchronous call, therefore you not have the data of the user when call it. I suppose you must make some like
ngOnInit(): void {
authenticationService.getUser().then((user)=>{
this.user = user;
this.userIsLoggedIn = this.user != undefined;
this.getParentFromUserEmail(this.user.email);
this.getSex();
}
});
}
Why would you want to create a two way binding on a method? You can just, in your template say
<p *ngIf="gender">Test</p>
Then you can just edit the gender in your component.ts file in order to change it on the template. No need for a getter.
I fixed the problem, I changed my return types from my service to promises. Also when our app launched we route instantly to login page since its a secure platform. The app component rendered the navbar which it shouldn't so everything from there was full of bugs. Thanks for the help, cheers.
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();
}