Jeremy
Jeremy

Reputation: 1288

How do I filter a list sorted from Checkbox Values in Angular?

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.

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

Answers (1)

Jorge Mussato
Jorge Mussato

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

Related Questions