Reputation: 1288
I've seen a few checkbox ideas and strategies, but can't seem to get this to work in my particular case.
I am newer to Angular but am trying to build a workout webapp. What I am trying to do is use checkboxes to filter which workouts appear in the workout list (workout list is displayed using an ngFor
).
Currently, I have a WorkoutListComponent that displays the list using the ngFor
(shortened for simplicity):
<div
*ngFor="let workout of workouts"
class="workout__list"
>
<h2 class="workout__item--title">{{ workout.title }}</h2>
<h3 class="workout__item--specs-heading">{{ workout.type }}</h3>
<h3 class="workout__item--specs-heading">{{ workout.duration}}</h3>
<h3 class="workout__item--specs-heading">{{ workout.phase }}</h3>
</div>
Within my ts
, I have the following:
...
workouts: any;
constructor(private workoutService: WorkoutService) {
this.workoutService.getWorkouts().subscribe(res => {
this.workouts = res;
console.log("List Comp: ", this.workouts);
});
}
The workouts: any
is coming from my workout.service
. This is the same service that holds my mock workout data and runs the function to get and sort workouts. The workout.service
is also making an observable with BehaviorSubject
so I can subscribe to it in the above WorkoutListComponent
.
WorkoutService
selectedWorkouts: BehaviorSubject<any>;
workouts: Workout[] = [
new Workout( ... dummy text for workouts ... ),
new Workout( ... dummy text for workouts ... ),
new Workout( ... dummy text for workouts ... )
];
constructor() {
this.selectedWorkouts = new BehaviorSubject(this.workouts);
}
getWorkouts() {
return this.selectedWorkouts.asObservable();
}
filterWorkouts(phase: any[]) {
console.log("SERVICE: ", phase);
// I want the filter to take place here and put the filtered items back into the `this.workouts` array
}
}
So far what I have tried code that doesn't work, where in the filterWorkouts()
function, I take in the phase and specialty, and then filter this.workouts
storing it into this.workouts
. Since this.workouts
is displayed in the list item, the idea is that this would work. Unfortunately it has not worked the way I wanted to. This code is at the bottom of the src/app/features/workouts-page/workoutservice/workout.service.ts
in the attached stackblitz.
The way I am currently receiving checkbox data is through my WorkoutFilter
component. I am taking the checked property keyvalues and storing them within an object array rather than only using true or false
properties as found in this stackoverflow . The WorkoutFilter Component is found in the stackblitz in src/app/features/workouts-page/workouts/workouts-filter
.
All I'm trying to do is find the best way to filter this list depending on the checkbox values.
The checkbox values are coming through the WorkoutFilter component.
The Workout list I want to display after being filtered is the WorkoutList.
I am currently using an apply button for the form. When submitted, the data is made into an object with strings to hold the values of the checkboxes. This can be seen in the WorkoutFilter component
If there is a better method to doing this, maybe even using id's , I am all ears.
Here is the StackBlitz code. I removed a few components such as the header and sidenav to make viewing of the issue easier. Thank you
Upvotes: 1
Views: 1269
Reputation: 2524
I recommend when you are working with multiple checkboxes, to use the SelectionModel from angular collections. Try not using reactive forms with multiple checkboxs control for filters like that, SelectionModel makes it so easier.
Here's the stackblitz, hope it helps.
https://stackblitz.com/edit/github-4rezan-yqxkyv
code:
html:
<h4>Phase</h4>
<div class="checkbox">
<span *ngFor="let phase of phaseOptions">
<mat-checkbox
color="warn"
type="checkbox"
(click)="phaseSelection.toggle(phase)"
>
{{ phase }}
</mat-checkbox>
</span>
</div>
<h4>Specialty</h4>
<div class="checkbox">
<span *ngFor="let specialty of specialtyOptions">
<mat-checkbox
color="warn"
type="checkbox"
(click)="specialtySelection.toggle(specialty)"
>
{{ specialty }}
</mat-checkbox>
</span>
</div>
<button (click)="applyFilter()">Apply</button>
ts:
specialtySelection = new SelectionModel<string>(true);
phaseSelection = new SelectionModel<string>(true);
applyFilter() {
this.workoutService.filterWorkouts(this.phaseSelection.selected,
this.specialtySelection.selected);
}
service:
filterWorkouts(phases: string[], specialties: string[]) {
// do Filter with phases and specialties
if (phases.length === 0 && specialties.length === 0) {
this.selectedWorkouts.next(workouts);
} else {
const byPhase = (workout) => phases.some(phase => workout.phase === phase);
const bySpecialty = (workout) => specialties.some(specialty => workout.specialty === specialty);
const workouts = this.workouts.filter(workout => byPhase(workout) && bySpecialty(workout));
this.selectedWorkouts.next(workouts);
}
}
Hope it helped.
Upvotes: 1