I have the following code, this is just a simple select one and select all. The problem is if I click select all, then I unclick one item, this still works fine, the select all checkbox is unchecked automatically and the single item is also unchecked.
Next, click select all again but the frontend checkbox doesn't display the previously unselected item checkbox as checked even if the select property is already set to true.
Any help is appreciated.
Here is my TS code:
//This is located in my model.ts
export class SomeClass{
id: number = 0;
selected: boolean = false;
}
//component code
original$: BehaviorSubject<SomeClass[]> = new BehaviorSubject<SomeClass[]>([]);
selected$: BehaviorSubject<SomeClass[]> = new BehaviorSubject<SomeClass[]>([]);
selectAllCheck:Boolean = false;
selectAll(event){
this.selectedPlans$ = new BehaviorSubject<SomeClass[]>([]);
if(event.target.checked)
{
this.selectAllCheck = true;
for (var i = 0; i < this.original$.value.length; i )
{
//this is correctly set to true but checkbox UI remains unchecked
this.original$.value[i].selected = true;
}
this.selected$.next([...this.original$.value]);
}
else{
this.selectAllCheck = false;
for (var i = 0; i < this.original$.value.length; i ) {
this.original$.value[i].selected = false;
}
}
}
selectOne(event, item) {
const checked = event.target.checked;
let list = this.selected$.value;
if (checked) {
list.push(item);
}
else {
this.selectAllCheck = false;
_.remove(list, x => x.id == item.id);
}
if(list.length == this.original$.value.length)
{
this.selectAllCheck = true;
}
this.selected$.next(list);
}
Here is my html code:
<input id="select All" type="checkbox" (change)="selectAll($event)"[checked]="selectAllCheck">
<tr *ngFor="let x of original$ | async; let index = index">
<input id="{{ index }}" type="checkbox" [checked]="x.selected"
(change)="selectOne($event, x)" />
</tr>
CodePudding user response:
Your issue is because you didnt call .next()
on your this.selected$
BehaviorSubject. async
pipe subscribes to your BehaviorSubject and returns the latest value it has emitted. And due to you didnt emit any - changing property in the array was not tracked.
Here is a working version of your code. Also a bit cleared out.
import { Component } from "@angular/core";
import { BehaviorSubject } from "rxjs";
import { map } from "rxjs/operators";
export class SomeClass {
id: number = 0;
selected: boolean = false;
}
const data: SomeClass[] = [
{ id: 1, selected: false },
{ id: 2, selected: false },
{ id: 3, selected: false },
{ id: 4, selected: false },
{ id: 5, selected: false }
];
@Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
original$ = new BehaviorSubject<SomeClass[]>(data);
selected$ = this.original$
.asObservable()
.pipe(map((x) => x.filter((item) => item.selected)));
selectAllCheck: boolean = false;
selectAll(event) {
this.selectAllCheck = event.target.checked;
this.original$.value.forEach((x) => (x.selected = this.selectAllCheck));
this.original$.next([...this.original$.value]);
}
selectOne(event, item) {
item.selected = event.target.checked;
this.original$.next([...this.original$.value]);
this.selectAllCheck = this.original$.value.every((x) => x.selected);
}
}
<div>
<div>
<p>Original$</p>
<input
id="select All"
type="checkbox"
(change)="selectAll($event)"
[checked]="selectAllCheck"
/>
<span>Check all</span>
<tr *ngFor="let x of original$ | async; let index = index">
<input
id="{{ index }}"
type="checkbox"
[checked]="x.selected"
(change)="selectOne($event, x)"
/>
<span>{{x.id}}</span>
</tr>
</div>
<div>
<p>Selected$</p>
<tr *ngFor="let x of selected$ | async; let index = index">
<span>{{x.id}}</span>
</tr>
</div>
</div>