Reputation: 1
I'm sorry if this was already answered but I couldn't find a sufficient solution anywhere. I have a parent component called "users-list" which renders a list of child components called "user" with *ngFor. The class of every component is dynamic and is dependent on several variables inside each component. When the user clicks on a component (and by that, "selects" it, its class changes. For now, the user can select multiple components and I need to make sure only one is selected at any given time. How can I achieve this?
Here's my code:
Parent Component:
import { Component, Input, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { User } from '../user';
import { UtilsService } from '../utils.service';
@Component({
selector: 'app-users-list',
templateUrl: './users-list.component.html',
styleUrls: ['./users-list.component.css']
})
export class UsersListComponent implements OnInit {
usersList : User[] = []
filteredUsersList : User[] = []
usersSub : Subscription = new Subscription
selectedClassNmae : String = "card-selected"
searchValue = ""
search(searchValue : string)
{
this.filteredUsersList = this.usersList.filter(x => x.name.includes(searchValue) || x.email.includes(searchValue))
}
constructor(private utils : UtilsService) { }
ngOnInit(): void {
this.usersSub = this.utils.getUsers().subscribe((data : any) =>
{
this.usersList = data
this.filteredUsersList = data
})
}
ngOnDestroy()
{
this.usersSub.unsubscribe();
}
}
<h1>Users List</h1>
<div>
<div class="searchBox">
<mat-form-field class="example-form-field" appearance="fill">
<mat-label>Search By Name</mat-label>
<input matInput type="text" [(ngModel)]="searchValue" (keyup)="search(searchValue)">
</mat-form-field>
</div>
<div class="addUserButton">
<button mat-button>Add User</button>
</div>
</div>
<app-user *ngFor="let user of filteredUsersList" [user]="user"></app-user>
Child Component:
import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core';
import { Subscription } from 'rxjs';
import { User } from '../user';
import { UtilsService } from '../utils.service';
@Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
@Input()
user : User | undefined
otherData : boolean = false
editData : boolean = false
constructor(private utils : UtilsService) { }
deleteSub : Subscription = new Subscription
deleteUser()
{
if(confirm("Are you sure you want to delete the user " + this.user!.name) == true)
{
this.deleteSub = this.utils.deleteUser(this.user!._id).subscribe()
}
}
editSub : Subscription = new Subscription
editUser()
{
this.editSub = this.utils.editUser(this.user!._id , this.user!).subscribe()
}
userSelected : Boolean = false
selectedUserID : String = ""
selectUser()
{
this.selectedUserID = this.user!._id
if(this.userSelected == false)
{
this.cardClassName = "card-selected"
}
else
{
this.cardClassName = this.originalClassName
}
this.userSelected = !this.userSelected
}
cardClassName : String = "card-completed"
originalClassName : String = "card-completed"
ngOnInit(): void {
this.user?.tasks.forEach(task => {
if(task.completed == false)
{
this.cardClassName = "card-uncompleted"
this.originalClassName = this.cardClassName
}
})
}
ngOnDestroy()
{
this.deleteSub.unsubscribe()
this.editSub.unsubscribe()
}
}
.card-completed {
max-width: 400px;
background-color: rgb(239, 248, 239);
border-style: solid;
border-color: #25a18e;
}
.card-uncompleted {
max-width: 400px;
background-color: rgb(248, 239, 239);
border-style: solid;
border-color: #bc4749;
}
.card-selected {
max-width: 400px;
background-color: rgb(248, 239, 222);
border-style: solid;
border-color: orange;
}
<mat-card class= "{{cardClassName}}" (click)="selectUser()">
<mat-card-content *ngIf="editData==false">
<mat-card-title>{{user?.name}}</mat-card-title>
<mat-card-subtitle>{{user?.email}}</mat-card-subtitle>
<mat-card-content *ngIf="otherData">
{{user?.street}} St.<br/>
{{user?.city}}<br/>
{{user?.zipcode}}
</mat-card-content>
<mat-card-actions>
<button mat-button (mouseover)="otherData=true" (click)="otherData=false">{{otherData? "Close Other Data" : "Other Data"}}</button>
<button mat-button (click)="editData=true">Edit</button>
<button mat-button (click)="deleteUser()">Delete</button>
</mat-card-actions>
</mat-card-content>
<mat-card-content *ngIf="editData==true">
<form #f="ngForm" (ngSubmit)="editUser()">
Full Name : <input type="text" name="fName" #fName="ngModel" [(ngModel)]="user!.name"> <br/>
Email : <input type="text" name="email" #email="ngModel" [(ngModel)]="user!.email"> <br/>
Street : <input type="text" name="street" #street="ngModel" [(ngModel)]="user!.street"> <br/>
City : <input type="text" name="city" #city="ngModel" [(ngModel)]="user!.city"> <br/>
Zipcode : <input type="number" name="zipcode" #zipcode="ngModel" [(ngModel)]="user!.zipcode"><br/><br/>
<input type="submit"> <input type="button" value="Cancel" (click)="editData=false">
</form>
</mat-card-content>
</mat-card>
<br/>
Upvotes: 0
Views: 567
Reputation: 31215
I would just use a variable to keep track of the selected element + [ngClass]
to conditionally add a CSS class :
Typescript :
export class AppComponent {
selectedElt: any;
items = [
{ id: 1, name: 'foo' },
{ id: 2, name: 'bar' },
{ id: 3, name: 'kix' },
];
}
HTML :
<ul>
<li
*ngFor="let item of items"
(click)="selectedElt = item"
[ngClass]="{ selected: selectedElt === item }"
>
{{ item.name }}
</li>
</ul>
CSS :
.selected {
color: red;
}
And the StackBlitz
Upvotes: 1