Filters

Angular Bootstrap 5 Filters plugin

Filters are the best way to select data that meets your requirements - they affect your search results by filtrating and sorting the dataset you pass to our component.

View full screen demo

Basic example

Filter: Color
Filter: Sale
...
Black Jeans Jacket

100$

Buy now
...
Black Jeans Jacket

100$

Buy now
...
Gray Jumper

80$

Buy now
...
Gray Jumper

80$

Buy now
...
Red Hoodie

120$

Buy now
...
Blue Jeans Jacket

90$

Buy now
<div class="container">
  <form [formGroup]="filtersGroup">
    <div class="col-md-6">
      <span class="fa-lg">Filter: Color</span>

      <div class="form-check mt-3">
        <input
          mdbInput
          class="form-check-input"
          type="radio"
          name="color"
          id="arrayRadio1"
          value="black"
          formControlName="color"
        />
        <label class="form-check-label" for="arrayRadio1">Black</label>
      </div>

      <div class="form-check">
        <input
          mdbInput
          class="form-check-input"
          type="radio"
          name="color"
          id="arrayRadio2"
          value="red"
          formControlName="color"
        />
        <label class="form-check-label" for="arrayRadio2">Red</label>
      </div>

      <div class="form-check">
        <input
          mdbInput
          class="form-check-input"
          type="radio"
          name="color"
          id="arrayRadio3"
          value="blue"
          formControlName="color"
        />
        <label class="form-check-label" for="arrayRadio3">Blue</label>
      </div>

      <div class="form-check">
        <input
          mdbInput
          class="form-check-input"
          type="radio"
          name="color"
          id="arrayRadio4"
          value="gray"
          formControlName="color"
        />
        <label class="form-check-label" for="arrayRadio4">Gray</label>
      </div>
    </div>

    <div class="col-md-6">
      <span class="fa-lg mb-5">Filter: Sale</span>

      <div class="form-check mt-3">
        <input
          mdbInput
          class="form-check-input"
          type="radio"
          name="sale"
          id="arrayRadio5"
          value="yes"
          formControlName="sale"
        />
        <label class="form-check-label" for="arrayRadio5">Yes</label>
      </div>

      <div class="form-check">
        <input
          mdbInput
          class="form-check-input"
          type="radio"
          name="sale"
          id="arrayRadio6"
          value="no"
          formControlName="sale"
        />
        <label class="form-check-label" for="arrayRadio6">No</label>
      </div>

      <button (click)="clearFilters()" type="button" class="btn btn-primary mt-3">
        Clear all filters
      </button>
    </div>
  </form>

  <div class="row text-center">
    <div *ngFor="let item of filteredItems$ | async" class="col-md-4 mt-3" style="max-width: 350px;">
      <div class="card shadow-2">
        <img [attr.src]="item.img" class="card-img-top" alt="..." />
        <div class="card-body">
          <h5 class="card-title">{{ item.product }}</h5>
          <p class="card-text">{{ item.price }} $</p>
          <a href="#" class="btn btn-primary ripple-surface">Buy now</a>
        </div>
      </div>
    </div>
  </div>
</div>
import { Component } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';

export interface Item {
  id: number;
  color: string;
  price: number;
  sale: string;
  product: string;
  img: string;
  size: number[];
}

export interface BasicFilters {
  color: string | null;
  sale: string | null;
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {
  items: Item[] = [
    {
      id: 1,
      color: 'black',
      price: 100,
      sale: 'no',
      product: 'Black Jeans Jacket',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/15.webp',
      size: [36, 40, 42],
    },
    {
      id: 2,
      color: 'black',
      price: 100,
      sale: 'no',
      product: 'Black Jeans Jacket',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/15.webp',
      size: [36, 38, 40, 42],
    },
    {
      id: 3,
      color: 'gray',
      price: 80,
      sale: 'yes',
      product: 'Gray Jumper',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/14.webp',
      size: [36, 38, 40],
    },
    {
      id: 4,
      color: 'gray',
      price: 80,
      sale: 'yes',
      product: 'Gray Jumper',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/14.webp',
      size: [40, 42],
    },
    {
      id: 5,
      color: 'red',
      price: 120,
      sale: 'yes',
      product: 'Red Hoodie',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/13.webp',
      size: [36, 40, 42],
    },
    {
      id: 6,
      color: 'blue',
      price: 90,
      sale: 'no',
      product: 'Blue Jeans Jacket',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/12.webp',
      size: [36, 38],
    },
  ];

  filteredItems$: Observable<Item[]>;
  filtersGroup: FormGroup;

  basicFilters: BasicFilters = {
    color: null,
    sale: null,
  };

  constructor() {
    this.filtersGroup = new FormGroup({
      color: new FormControl(),
      sale: new FormControl(),
    });

    this.filteredItems$ = this.filtersGroup.valueChanges.pipe(
      startWith(this.basicFilters),
      map((filters: BasicFilters) => {
        const { color, sale } = filters;
        let items = this.items;

        if (color) {
          items = items.filter((item) => item.color === color);
        }

        if (sale) {
          items = items.filter((item) => item.sale === sale);
        }

        return items;
      })
    );
  }

  clearFilters() {
    this.filtersGroup.reset();
  }
}

Checkbox example

Note: If there is more than one option, the result of filtering will show items that match both of them.

Filter: Color
Filter: Sale
Black Jeans Jacket
Black Jeans Jacket

100$

Buy now
Gray Jumper
Gray Jumper

100$ 80$

Buy now
Blue Jeans Jacket
Blue Jeans Jacket

90$

Buy now
Red Hoodie
Red Hoodie

150$ 120$

Buy now
Black Jeans Jacket
Black Jeans Jacket

100$

Buy now
Gray Jumper
Gray Jumper

100$ 80$

Buy now
<div class="container">
  <form [formGroup]="filtersGroup">
    <div class="col-md-6">
      <span class="fa-lg">Filter: Color</span>

      <div class="form-check mt-3">
        <input
          mdbInput
          class="form-check-input"
          type="checkbox"
          name="color"
          id="arrayCheckbox1"
          value="black"
          formControlName="black"
        />
        <label class="form-check-label" for="arrayCheckbox1">Black</label>
      </div>

      <div class="form-check">
        <input
          mdbInput
          class="form-check-input"
          type="checkbox"
          name="color"
          id="arrayCheckbox2"
          value="red"
          formControlName="red"
        />
        <label class="form-check-label" for="arrayCheckbox2">Red</label>
      </div>

      <div class="form-check">
        <input
          mdbInput
          class="form-check-input"
          type="checkbox"
          name="color"
          id="arrayCheckbox3"
          value="blue"
          formControlName="blue"
        />
        <label class="form-check-label" for="arrayCheckbox3">Blue</label>
      </div>

      <div class="form-check">
        <input
          mdbInput
          class="form-check-input"
          type="checkbox"
          name="color"
          id="arrayCheckbox4"
          value="gray"
          formControlName="gray"
        />
        <label class="form-check-label" for="arrayCheckbox4">Gray</label>
      </div>
    </div>

    <div class="col-md-6">
      <span class="fa-lg mb-5">Filter: Sale</span>

      <div class="form-check mt-3">
        <input
          mdbInput
          class="form-check-input"
          type="checkbox"
          name="sale"
          id="arrayCheckbox5"
          value="yes"
          formControlName="saleYes"
        />
        <label class="form-check-label" for="arrayCheckbox5">Yes</label>
      </div>

      <div class="form-check">
        <input
          mdbInput
          class="form-check-input"
          type="checkbox"
          name="sale"
          id="arrayCheckbox6"
          value="no"
          formControlName="saleNo"
        />
        <label class="form-check-label" for="arrayCheckbox6">No</label>
      </div>

      <button (click)="clearFilters()" type="button" class="btn btn-primary mt-3">
        Clear all filters
      </button>
    </div>
  </form>

  <div class="row text-center">
    <div *ngFor="let item of filteredItems$ | async" class="col-md-4 mt-3"  style="max-width: 350px;">
      <div class="card shadow-2">
        <img [attr.src]="item.img" class="card-img-top" alt="..." />
        <div class="card-body">
          <h5 class="card-title">{{ item.product }}</h5>
          <p class="card-text">{{ item.price }} $</p>
          <a href="#" class="btn btn-primary ripple-surface">Buy now</a>
        </div>
      </div>
    </div>
  </div>
</div>
import { Component } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';

export interface Item {
  id: number;
  color: string;
  price: number;
  sale: string;
  product: string;
  img: string;
  size: number[];
}
export interface Filters {
  [key: string]: string | null;
  black: string | null;
  red: string | null;
  blue: string | null;
  gray: string | null;
  saleYes: string | null;
  saleNo: string | null;
}


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {
  items: Item[] = [
    {
      id: 1,
      color: 'black',
      price: 100,
      sale: 'no',
      product: 'Black Jeans Jacket',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/15.webp',
      size: [36, 40, 42],
    },
    {
      id: 2,
      color: 'black',
      price: 100,
      sale: 'no',
      product: 'Black Jeans Jacket',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/15.webp',
      size: [36, 38, 40, 42],
    },
    {
      id: 3,
      color: 'gray',
      price: 80,
      sale: 'yes',
      product: 'Gray Jumper',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/14.webp',
      size: [36, 38, 40],
    },
    {
      id: 4,
      color: 'gray',
      price: 80,
      sale: 'yes',
      product: 'Gray Jumper',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/14.webp',
      size: [40, 42],
    },
    {
      id: 5,
      color: 'red',
      price: 120,
      sale: 'yes',
      product: 'Red Hoodie',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/13.webp',
      size: [36, 40, 42],
    },
    {
      id: 6,
      color: 'blue',
      price: 90,
      sale: 'no',
      product: 'Blue Jeans Jacket',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/12.webp',
      size: [36, 38],
    },
  ];

  filteredItems$: Observable<Item[]>;
  filtersGroup: FormGroup;

  defaultFilters: Filters = {
    black: null,
    red: null,
    blue: null,
    gray: null,
    saleYes: null,
    saleNo: null,
  };

  constructor() {
    this.filtersGroup = new FormGroup({
      black: new FormControl(),
      red: new FormControl(),
      blue: new FormControl(),
      gray: new FormControl(),
      saleYes: new FormControl(),
      saleNo: new FormControl(),
    });

    this.filteredItems$ = this.filtersGroup.valueChanges.pipe(
      startWith(this.defaultFilters),
      map((controls: Filters) => {
        let items = this.items;
        const colors = Object.keys(controls).filter(
          (control) => controls[control] && !control.startsWith('sale')
        );

        const sales = Object.keys(controls)
          .filter((control) => controls[control] && control.startsWith('sale'))
          .map((string) => string.toLowerCase().replace('sale', ''));

        if (colors.length > 0) {
          items = items.filter((item) => colors.some((color) => item.color === color));
        }

        if (sales.length > 0) {
          items = items.filter((item) => sales.some((sale) => item.sale === sale));
        }
        return items;
      })
    );

  }

  clearFilters() {
    this.filtersGroup.reset();
  }
}

Size example

Note: If there is more than one option, the result of filtering will show items that match both of them.

Filter: Size
Filter: Sale
Black Jeans Jacket
Black Jeans Jacket

100$

Buy now
Gray Jumper
Gray Jumper

100$ 80$

Buy now
Blue Jeans Jacket
Blue Jeans Jacket

90$

Buy now
Red Hoodie
Red Hoodie

150$ 120$

Buy now
Black Jeans Jacket
Black Jeans Jacket

100$

Buy now
Gray Jumper
Gray Jumper

100$ 80$

Buy now
<div class="container">
  <form [formGroup]="filtersGroup">
    <div class="col-md-6">
      <span class="fa-lg">Filter: Size</span>
      <div class="form-check mt-3">
        <input
          mdbInput
          class="form-check-input"
          type="checkbox"
          name="size"
          id="arrayCheckboxSize1"
          value="36"
          formControlName="36"
        />
        <label class="form-check-label" for="arrayCheckboxSize1">36</label>
      </div>

      <div class="form-check">
        <input
          mdbInput
          class="form-check-input"
          type="checkbox"
          name="size"
          id="arrayCheckboxSize2"
          value="38"
          formControlName="38"
        />
        <label class="form-check-label" for="arrayCheckboxSize2">38</label>
      </div>

      <div class="form-check">
        <input
          mdbInput
          class="form-check-input"
          type="checkbox"
          name="size"
          id="arrayCheckboxSize3"
          value="40"
          formControlName="40"
        />
        <label class="form-check-label" for="arrayCheckboxSize3">40</label>
      </div>

      <div class="form-check">
        <input
          mdbInput
          class="form-check-input"
          type="checkbox"
          name="size"
          id="arrayCheckboxSize4"
          value="42"
          formControlName="42"
        />
        <label class="form-check-label" for="arrayCheckboxSize4">42</label>
      </div>
    </div>

    <div class="col-md-6">
      <span class="fa-lg mb-5">Filter: Sale</span>

      <div class="form-check mt-3">
        <input
          mdbInput
          class="form-check-input"
          type="radio"
          name="sale"
          id="arrayCheckboxSize5"
          value="yes"
          formControlName="sale"
        />
        <label class="form-check-label" for="arrayCheckboxSize5">Yes</label>
      </div>

      <div class="form-check">
        <input
          mdbInput
          class="form-check-input"
          type="radio"
          name="sale"
          id="arrayCheckboxSize6"
          value="no"
          formControlName="sale"
        />
        <label class="form-check-label" for="arrayCheckboxSize6">No</label>
      </div>

      <button (click)="clearFilters()" type="button" class="btn btn-primary mt-3">
        Clear all filters
      </button>
    </div>
  </form>

  <div class="row text-center">
    <div *ngFor="let item of filteredItems$ | async" class="col-md-4 mt-3"  style="max-width: 350px;">
      <div class="card shadow-2">
        <img [attr.src]="item.img" class="card-img-top" alt="..." />
        <div class="card-body">
          <h5 class="card-title">{{ item.product }}</h5>
          <p class="card-text">{{ item.price }} $</p>
          <a href="#" class="btn btn-primary ripple-surface">Buy now</a>
        </div>
      </div>
    </div>
  </div>
</div>
import { Component } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';

export interface Item {
  id: number;
  color: string;
  price: number;
  sale: string;
  product: string;
  img: string;
  size: number[];
}
export interface Filters {
  [key: string]: string | number | null;
  36: number | null;
  38: number | null;
  40: number | null;
  42: number | null;
  sale: string | null;
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {
  items: Item[] = [
    {
      id: 1,
      color: 'black',
      price: 100,
      sale: 'no',
      product: 'Black Jeans Jacket',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/15.webp',
      size: [36, 40, 42],
    },
    {
      id: 2,
      color: 'black',
      price: 100,
      sale: 'no',
      product: 'Black Jeans Jacket',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/15.webp',
      size: [36, 38, 40, 42],
    },
    {
      id: 3,
      color: 'gray',
      price: 80,
      sale: 'yes',
      product: 'Gray Jumper',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/14.webp',
      size: [36, 38, 40],
    },
    {
      id: 4,
      color: 'gray',
      price: 80,
      sale: 'yes',
      product: 'Gray Jumper',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/14.webp',
      size: [40, 42],
    },
    {
      id: 5,
      color: 'red',
      price: 120,
      sale: 'yes',
      product: 'Red Hoodie',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/13.webp',
      size: [36, 40, 42],
    },
    {
      id: 6,
      color: 'blue',
      price: 90,
      sale: 'no',
      product: 'Blue Jeans Jacket',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/12.webp',
      size: [36, 38],
    },
  ];

  filteredItems$: Observable<Item[]>;
  filtersGroup: FormGroup;


  defaultFilters: Filters = {
    36: null,
    38: null,
    40: null,
    42: null,
    sale: null,
  };


  constructor() {
    this.filtersGroup = new FormGroup({
      36: new FormControl(),
      38: new FormControl(),
      40: new FormControl(),
      42: new FormControl(),
      sale: new FormControl(),
    });

    this.filteredItems$ = this.filtersGroup.valueChanges.pipe(
      startWith(this.defaultFilters),
      map((controls: Filters) => {
        let items = this.items;
        const sizes = Object.keys(controls)
          .filter((control) => controls[control] && !control.startsWith('sale'))
          .map((size) => Number(size));

        const sale = controls.sale;

        if (sizes.length > 0) {
          items = items.filter((item) =>
            sizes.some((checkboxSize) => item['size'].some((itemSize) => itemSize === checkboxSize))
          );
        }

        if (sale) {
          items = items.filter((item) => item.sale === sale);
        }
        return items;
      })
    );
  }

  clearFilters() {
    this.filtersGroup.reset();
  }
}

Spinner & Asynchronous Data example

Price:

...
Fantasy T-shirt

12.99$

Buy now
...
Fantasy T-shirt

12.99$

Buy now
...
Fantasy T-shirt

40.99$

Buy now
...
Denim Jacket

40.99$

Buy now
...
Ripped jeans

20.99$

Buy now
...
Boyfriend jeans

20.99$

Buy now
...
Ripped sweatshirt

29.99$

Buy now
...
Longsleeve

120.99$

Buy now
...
Stripped sweatshirt

32.99$

Buy now
...
Red chinos

62.99$

Buy now
...
Camel coat

62.99$

Buy now
...
Blue jeans

42.99$

Buy now
...
Orange T-shirt

12.99$

Buy now
...
Ballerina skirt

12.99$

Buy now
<h2 class="mb-4">Async example</h2>
<section class="border p-4 d-flex flex-column justify-content-center align-items-center mb-4">
  <p class="text-center fa-lg fw-bold">Price:</p>

  <form [formGroup]="filtersGroup">
    <div class="col-md-6 mt-3 d-flex justify-content-between text-center">
      <mdb-form-control>
        <input
          mdbInput
          type="number"
          id="min"
          name="min"
          class="form-control"
          formControlName="min"
        />
        <label mdbLabel class="form-label" for="min">Min</label>
      </mdb-form-control>

      <mdb-form-control>
        <input
          mdbInput
          type="number"
          id="max"
          name="max"
          class="form-control"
          formControlName="max"
        />
        <label mdbLabel class="form-label" for="max">Max</label>
      </mdb-form-control>
    </div>
  </form>
  <div *ngIf="loading" style="height: 300px; width: 100%; z-index: 1029" #container>
    <mdb-loading [backdrop]="false" [show]="true" [container]="container">
      <div class="loading-spinner">
        <div class="spinner-grow loading-icon" role="status"></div>
      </div>
    </mdb-loading>
  </div>
  <div class="row text-center">
    <div *ngFor="let item of filteredItems$ | async" class="col-md-4 mt-3"  style="max-width: 350px;">
      <div *ngIf="!loading" class="card shadow-2">
        <img [attr.src]="item.image" class="card-img-top" alt="..." />

        <div class="card-body">
          <h5 class="card-title">{{ item.product }}</h5>
          <p class="card-text">$ {{ item.price }}</p>
          <a href="#" class="btn btn-primary ripple-surface">Buy now</a>
        </div>
      </div>
    </div>

    <!--Section: Docs content-->
  </div>
import { HttpClient } from '@angular/common/http';
import { Component } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { Observable } from 'rxjs';
import { delay, map, startWith, switchMap } from 'rxjs/operators';

export interface Item {
  id: number;
  color: string;
  price: number;
  sale: string;
  product: string;
  image: string;
  size: number[];
}

export interface Filters {
  min: number | null;
  max: number | null;
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {
  loading = true;

  filteredItems$: Observable<Item[]>;
  filtersGroup: FormGroup;

  defaultFilters: Filters = {
    min: null,
    max: null,
  };

  constructor(private http: HttpClient) {
    this.filtersGroup = new FormGroup({
      min: new FormControl(),
      max: new FormControl(),
    });

    this.filteredItems$ = this.filtersGroup.valueChanges.pipe(
      startWith(this.defaultFilters),
      switchMap((filters: Filters) => {
        this.loading = true;
        return http.get<Item[]>('/assets/products.json').pipe(
          delay(600),
          map((data: Item[]) => {
            const { min, max } = filters;
            let items: Item[] = data;
            this.loading = false;
            if (min && max) {
              items = data.filter((item) => item['price'] >= min && item['price'] <= max);
              return items;
            }
            if (min) {
              items = data.filter((item) => item['price'] >= min);
              return items;
            }
            if (max) {
              items = data.filter((item) => item['price'] <= max);
              return items;
            }

            return items;
          })
        );
      })
    );
  }

  clearFilters() {
    this.filtersGroup.reset();
  }
}
[
{
  "id": 1,
  "category": "shirts",
  "name": "Fantasy T-shirt",
  "rating": 4,
  "image": "https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/12.webp",
  "description": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.",
  "available": true,
  "size": ["34", "36", "40"],
  "condition": "new",
  "color": "blue",
  "price": 12.99,
  "keywords": ["t-shirt", "sweatshitrt"],
  "discount": 0,
  "gender": "male"
},
{
  "id": 2,
  "category": "shirts",
  "name": "Fantasy T-shirt",
  "rating": 5,
  "image": "https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/13.webp",
  "description": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.",
  "available": true,
  "size": ["34", "36", "40", "44"],
  "condition": "new",
  "color": "red",
  "price": 12.99,
  "discount": 0,
  "gender": "male"
},
{
  "id": 3,
  "category": "shirts",
  "name": "Fantasy T-shirt",
  "rating": 3,
  "image": "https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/14.webp",
  "description": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.",
  "available": true,
  "size": ["34", "36"],
  "condition": "new",
  "color": "grey",
  "price": 40.99,
  "discount": 10,
  "gender": "male"
},
{
  "id": 4,
  "category": "jackets",
  "name": "Denim Jacket",
  "rating": 5,
  "image": "https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/15.webp",
  "description": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.",
  "available": true,
  "condition": "new",
  "color": "grey",
  "price": 40.99,
  "discount": 0,
  "gender": "unisex"
},
{
  "id": 5,
  "category": "jeans",
  "name": "Ripped jeans",
  "rating": 5,
  "image": "https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/11.webp",
  "description": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.",
  "available": true,
  "size": ["34", "36", "38", "40"],
  "condition": "renewed",
  "color": "blue",
  "price": 20.99,
  "discount": 5,
  "gender": "female"
},
{
  "id": 6,
  "category": "jeans",
  "name": "Boyfriend jeans",
  "rating": 4,
  "image": "https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/10.webp",
  "description": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.",
  "available": false,
  "size": ["34", "36", "38", "40"],
  "condition": "used",
  "color": "blue",
  "price": 20.99,
  "discount": 5,
  "gender": "female"
},
{
  "id": 7,
  "category": "shirts",
  "name": "Ripped sweatshirt",
  "rating": 4,
  "image": "https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/7.webp",
  "description": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.",
  "available": true,
  "size": ["34", "36", "38", "40"],
  "condition": "collectible",
  "color": "white",
  "price": 29.99,
  "discount": 5,
  "gender": "female"
},
{
  "id": 8,
  "category": "shirts",
  "name": "Longsleeve",
  "rating": 4,
  "image": "https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/8.webp",
  "description": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.",
  "available": true,
  "size": ["40"],
  "condition": "collectible",
  "color": "black",
  "price": 120.99,
  "discount": 0,
  "gender": "male"
},
{
  "id": 8,
  "category": "shirts",
  "name": "Stripped sweatshirt",
  "rating": 4,
  "image": "https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/6.webp",
  "description": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.",
  "available": true,
  "size": ["40", "38", "36"],
  "condition": "new",
  "color": "white",
  "price": 32.99,
  "discount": 10,
  "gender": "female"
},
{
  "id": 9,
  "category": "trousers",
  "name": "Red chinos",
  "rating": 5,
  "image": "https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/5.webp",
  "description": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.",
  "available": true,
  "size": ["40", "38", "36"],
  "condition": "new",
  "color": "red",
  "price": 62.99,
  "discount": 10,
  "gender": "female"
},
{
  "id": 10,
  "category": "coats",
  "name": "Camel coat",
  "rating": 5,
  "image": "https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/4.webp",
  "description": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.",
  "available": true,
  "size": ["40", "38", "36"],
  "condition": "used",
  "color": "brown",
  "price": 62.99,
  "discount": 10,
  "gender": "female"
},
{
  "id": 11,
  "category": "jeans",
  "name": "Blue jeans",
  "rating": 5,
  "image": "https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/3.webp",
  "description": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.",
  "available": true,
  "size": ["40", "38", "36"],
  "condition": "new",
  "color": "blue",
  "price": 42.99,
  "discount": 0,
  "gender": "female"
},
{
  "id": 12,
  "category": "shirts",
  "name": "Orange T-shirt",
  "rating": 3,
  "image": "https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/3.webp",
  "description": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.",
  "available": true,
  "size": ["40", "38", "36"],
  "condition": "new",
  "color": "orange",
  "price": 12.99,
  "discount": 0,
  "gender": "female"
},
{
  "id": 13,
  "category": "skirts",
  "name": "Ballerina skirt",
  "rating": 4,
  "image": "https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/2.webp",
  "description": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.",
  "available": true,
  "size": ["38", "36"],
  "condition": "collectible",
  "color": "white",
  "price": 12.99,
  "discount": 0,
  "gender": "female"
}
  ]

Animations

The full list of available animations you can find here.

Filter: Color
Filter: Sale
...
Black Jeans Jacket

100$

Buy now
...
Black Jeans Jacket

100$

Buy now
...
Gray Jumper

80$

Buy now
...
Gray Jumper

80$

Buy now
...
Red Hoodie

120$

Buy now
...
Blue Jeans Jacket

90$

Buy now
<div class="container">
  <form [formGroup]="filtersGroup">
    <div class="col-md-6">
      <span class="fa-lg">Filter: Color</span>

      <div class="form-check mt-3">
        <input
          mdbInput
          class="form-check-input"
          type="radio"
          name="color"
          id="arrayRadio10"
          value="black"
          formControlName="color"
        />
        <label class="form-check-label" for="arrayRadio10">Black</label>
      </div>

      <div class="form-check">
        <input
          mdbInput
          class="form-check-input"
          type="radio"
          name="color"
          id="arrayRadio20"
          value="red"
          formControlName="color"
        />
        <label class="form-check-label" for="arrayRadio20">Red</label>
      </div>

      <div class="form-check">
        <input
          mdbInput
          class="form-check-input"
          type="radio"
          name="color"
          id="arrayRadio30"
          value="blue"
          formControlName="color"
        />
        <label class="form-check-label" for="arrayRadio30">Blue</label>
      </div>

      <div class="form-check">
        <input
          mdbInput
          class="form-check-input"
          type="radio"
          name="color"
          id="arrayRadio40"
          value="gray"
          formControlName="color"
        />
        <label class="form-check-label" for="arrayRadio40">Gray</label>
      </div>
    </div>

    <div class="col-md-6">
      <span class="fa-lg mb-5">Filter: Sale</span>

      <div class="form-check mt-3">
        <input
          mdbInput
          class="form-check-input"
          type="radio"
          name="sale"
          id="arrayRadio50"
          value="yes"
          formControlName="sale"
        />
        <label class="form-check-label" for="arrayRadio50">Yes</label>
      </div>

      <div class="form-check">
        <input
          mdbInput
          class="form-check-input"
          type="radio"
          name="sale"
          id="arrayRadio60"
          value="no"
          formControlName="sale"
        />
        <label class="form-check-label" for="arrayRadio60">No</label>
      </div>

      <button (click)="clearFilters()" type="button" class="btn btn-primary mt-3">
        Clear all filters
      </button>
    </div>
  </form>

  <div class="row text-center">
    <div *ngFor="let item of filteredItems$ | async" class="col-md-4 mt-3"  style="max-width: 350px;">
      <div
        class="card shadow-2"
        [@zoomIn]="animationState"
        (@zoomIn.done)="onAnimationDone()"
      >
        <img [attr.src]="item.img" class="card-img-top" alt="..." />
        <div class="card-body">
          <h5 class="card-title">{{ item.product }}</h5>
          <p class="card-text">{{ item.price }} $</p>
          <a href="#" class="btn btn-primary ripple-surface">Buy now</a>
        </div>
      </div>
    </div>
  </div>
</div>
import { Component } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { Observable } from 'rxjs';
import { map, startWith, tap } from 'rxjs/operators';
import { zoomInAnimation } from 'mdb-angular-ui-kit/animations';

export interface Item {
  id: number;
  color: string;
  price: number;
  sale: string;
  product: string;
  img: string;
  size: number[];
}

export interface Filters {
  color: string | null;
  sale: string | null;
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  animations: [zoomInAnimation()],
})
export class AppComponent {
  animationState = false;

  items: Item[] = [
    {
      id: 1,
      color: 'black',
      price: 100,
      sale: 'no',
      product: 'Black Jeans Jacket',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/15.webp',
      size: [36, 40, 42],
    },
    {
      id: 2,
      color: 'black',
      price: 100,
      sale: 'no',
      product: 'Black Jeans Jacket',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/15.webp',
      size: [36, 38, 40, 42],
    },
    {
      id: 3,
      color: 'gray',
      price: 80,
      sale: 'yes',
      product: 'Gray Jumper',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/14.webp',
      size: [36, 38, 40],
    },
    {
      id: 4,
      color: 'gray',
      price: 80,
      sale: 'yes',
      product: 'Gray Jumper',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/14.webp',
      size: [40, 42],
    },
    {
      id: 5,
      color: 'red',
      price: 120,
      sale: 'yes',
      product: 'Red Hoodie',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/13.webp',
      size: [36, 40, 42],
    },
    {
      id: 6,
      color: 'blue',
      price: 90,
      sale: 'no',
      product: 'Blue Jeans Jacket',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/12.webp',
      size: [36, 38],
    },
  ];

  filteredItems$: Observable<Item[]>;
  filtersGroup: FormGroup;

  defaultFilters: Filters = {
    color: null,
    sale: null,
  };
  constructor() {
    this.filtersGroup = new FormGroup({
      color: new FormControl(),
      sale: new FormControl(),
    });

    this.filteredItems$ = this.filtersGroup.valueChanges.pipe(
      startWith(this.defaultFilters),
      map((filters: Filters) => {
        const { color, sale } = filters;
        let items = this.items;
        if (color) {
          items = items.filter((item) => item.color === color);
        }

        if (sale) {
          items = items.filter((item) => item.sale === sale);
        }

        return items;
      }),
      tap(() =>
        setTimeout(() => {
          this.animationState = !this.animationState;
        }, 0)
      )
    );
  }

  clearFilters() {
    this.filtersGroup.reset();
  }

  onAnimationDone() {
    this.animationState = false;
  }
}

Filter and sort

Filter: Color
Filter: Sale
Choose category
...
Black Jeans Jacket

100$

Buy now
...
Black Jeans Jacket

100$

Buy now
...
Gray Jumper

80$

Buy now
...
Gray Jumper

80$

Buy now
...
Red Hoodie

120$

Buy now
...
Blue Jeans Jacket

90$

Buy now
<div class="container">
  <form [formGroup]="filtersGroup">
    <div class="col-md-6">
      <span class="fa-lg">Filter: Color</span>

      <div class="form-check mt-3">
        <input
          mdbInput
          class="form-check-input"
          type="radio"
          name="color"
          id="arrayRadioSort1"
          value="black"
          formControlName="color"
        />
        <label class="form-check-label" for="arrayRadioSort1">Black</label>
      </div>

      <div class="form-check">
        <input
          mdbInput
          class="form-check-input"
          type="radio"
          name="color"
          id="arrayRadioSort2"
          value="red"
          formControlName="color"
        />
        <label class="form-check-label" for="arrayRadioSort2">Red</label>
      </div>

      <div class="form-check">
        <input
          mdbInput
          class="form-check-input"
          type="radio"
          name="color"
          id="arrayRadioSort3"
          value="blue"
          formControlName="color"
        />
        <label class="form-check-label" for="arrayRadioSort3">Blue</label>
      </div>

      <div class="form-check">
        <input
          mdbInput
          class="form-check-input"
          type="radio"
          name="color"
          id="arrayRadioSort4"
          value="gray"
          formControlName="color"
        />
        <label class="form-check-label" for="arrayRadioSort4">Gray</label>
      </div>
    </div>

    <div class="col-md-6 my-3">
      <span class="fa-lg mb-5">Filter: Sale</span>

      <div class="form-check mt-3">
        <input
          mdbInput
          class="form-check-input"
          type="radio"
          name="sale"
          id="arrayRadioSort5"
          value="yes"
          formControlName="sale"
        />
        <label class="form-check-label" for="arrayRadioSort5">Yes</label>
      </div>

      <div class="form-check">
        <input
          mdbInput
          class="form-check-input"
          type="radio"
          name="sale"
          id="arrayRadioSort6"
          value="no"
          formControlName="sale"
        />
        <label class="form-check-label" for="arrayRadioSort6">No</label>
      </div>

      <button (click)="clearFilters()" type="button" class="btn btn-primary mt-3">
        Clear all filters
      </button>
    </div>

    <mdb-form-control>
      <mdb-select formControlName="sorting">
        <mdb-option *ngFor="let option of options" [value]="option.value">{{
          option.label
        }}</mdb-option>
      </mdb-select>
      <label mdbLabel class="form-label">Sort</label>
    </mdb-form-control>
  </form>

  <div class="row text-center">
    <div *ngFor="let item of filteredItems$ | async" class="col-md-4 mt-3"  style="max-width: 350px;">
      <div class="card shadow-2">
        <img [attr.src]="item.img" class="card-img-top" alt="..." />
        <div class="card-body">
          <h5 class="card-title">{{ item.product }}</h5>
          <p class="card-text">{{ item.price }} $</p>
          <a href="#" class="btn btn-primary ripple-surface">Buy now</a>
        </div>
      </div>
    </div>
  </div>
</div>
import { Component } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { Observable } from 'rxjs';
import { map, startWith, } from 'rxjs/operators';

export interface Item {
  id: number;
  color: string;
  price: number;
  sale: string;
  product: string;
  img: string;
  size: number[];
}
export interface Filters {
  color: string | null;
  sale: string | null;
  sorting: string | null;
}


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {
  options = [
    { value: 'productasc', label: 'Product name ascending' },
    { value: 'productdesc', label: 'Product name descending' },
    { value: 'pricedesc', label: 'Highest price' },
    { value: 'priceasc', label: 'Lowest price' },
  ];

  items: Item[] = [
    {
      id: 1,
      color: 'black',
      price: 100,
      sale: 'no',
      product: 'Black Jeans Jacket',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/15.webp',
      size: [36, 40, 42],
    },
    {
      id: 2,
      color: 'black',
      price: 100,
      sale: 'no',
      product: 'Black Jeans Jacket',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/15.webp',
      size: [36, 38, 40, 42],
    },
    {
      id: 3,
      color: 'gray',
      price: 80,
      sale: 'yes',
      product: 'Gray Jumper',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/14.webp',
      size: [36, 38, 40],
    },
    {
      id: 4,
      color: 'gray',
      price: 80,
      sale: 'yes',
      product: 'Gray Jumper',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/14.webp',
      size: [40, 42],
    },
    {
      id: 5,
      color: 'red',
      price: 120,
      sale: 'yes',
      product: 'Red Hoodie',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/13.webp',
      size: [36, 40, 42],
    },
    {
      id: 6,
      color: 'blue',
      price: 90,
      sale: 'no',
      product: 'Blue Jeans Jacket',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/12.webp',
      size: [36, 38],
    },
  ];

  filteredItems$: Observable<Item[]>;

  filtersGroup: FormGroup;

  sortDefaultFilters: Filters = {
    color: null,
    sale: null,
    sorting: null,
  };

  constructor() {
    this.filtersGroup = new FormGroup({
      color: new FormControl(),
      sale: new FormControl(),
      sorting: new FormControl(),
    });

    this.filteredItems$ = this.filtersGroup.valueChanges.pipe(
      startWith(this.sortDefaultFilters),
      map((filters: Filters) => {
        const { color, sale, sorting } = filters;
        let items = this.items;

        if (color) {
          items = items.filter((item) => item.color === color);
        }

        if (sale) {
          items = items.filter((item) => item.sale === sale);
        }

        if (sorting && sorting.startsWith('product')) {
          items = items.sort((a, b) =>
            sorting.endsWith('asc')
              ? a['product'].localeCompare(b['product'])
              : -1 * a['product'].localeCompare(b['product'])
          );
        }

        if (sorting && sorting.startsWith('price')) {
          items = items.sort((a, b) =>
            sorting.endsWith('asc') ? a['price'] - b['price'] : b['price'] - a['price']
          );
        }
        return items;
      })
    );
  }

  clearFilters() {
    this.filtersGroup.reset();
  }
}

Range example

Price:

Current: 80$

Current: 120$

Black Jeans Jacket
Black Jeans Jacket

100$

Buy now
Gray Jumper
Gray Jumper

100$ 80$

Buy now
Blue Jeans Jacket
Blue Jeans Jacket

90$

Buy now
Red Hoodie
Red Hoodie

150$ 120$

Buy now
Black Jeans Jacket
Black Jeans Jacket

100$

Buy now
Gray Jumper
Gray Jumper

100$ 80$

Buy now
<div class="container">
  <form [formGroup]="filtersGroup">
    <div class="col-md-6">
      <mdb-range formControlName="min" [min]="80" [max]="120"></mdb-range>
      Current min ${{ rangeMin }}
      <mdb-range formControlName="max" [min]="80" [max]="120"></mdb-range>
      Current max ${{ rangeMax }}
    </div>
  </form>

  <div class="row text-center">
    <div *ngFor="let item of filteredItems$ | async" class="col-md-4 mt-3">
      <div class="card shadow-2">
        <img [attr.src]="item.img" class="card-img-top" alt="..." />
        <div class="card-body">
          <h5 class="card-title">{{ item.product }}</h5>
          <p class="card-text">{{ item.price }} $</p>
          <a href="#" class="btn btn-primary ripple-surface">Buy now</a>
        </div>
      </div>
    </div>
  </div>
</div>
import { Component } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';

export interface Item {
  id: number;
  color: string;
  price: number;
  sale: string;
  product: string;
  img: string;
  size: number[];
}

export interface Filters {
  min: number;
  max: number;
}


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {
  items: Item[] = [
    {
      id: 1,
      color: 'black',
      price: 100,
      sale: 'no',
      product: 'Black Jeans Jacket',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/15.webp',
      size: [36, 40, 42],
    },
    {
      id: 2,
      color: 'black',
      price: 100,
      sale: 'no',
      product: 'Black Jeans Jacket',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/15.webp',
      size: [36, 38, 40, 42],
    },
    {
      id: 3,
      color: 'gray',
      price: 80,
      sale: 'yes',
      product: 'Gray Jumper',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/14.webp',
      size: [36, 38, 40],
    },
    {
      id: 4,
      color: 'gray',
      price: 80,
      sale: 'yes',
      product: 'Gray Jumper',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/14.webp',
      size: [40, 42],
    },
    {
      id: 5,
      color: 'red',
      price: 120,
      sale: 'yes',
      product: 'Red Hoodie',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/13.webp',
      size: [36, 40, 42],
    },
    {
      id: 6,
      color: 'blue',
      price: 90,
      sale: 'no',
      product: 'Blue Jeans Jacket',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/12.webp',
      size: [36, 38],
    },
  ];

  filteredItems$: Observable<Item[]>;
  filtersGroup: FormGroup;

  rangeMin = 80;
  rangeMax = 120;

  defaultFilters: Filters = {
    min: 80,
    max: 120,
  };


  constructor() {
    this.filtersGroup = new FormGroup({
      min: new FormControl(80),
      max: new FormControl(120),
    });

    this.filteredItems$ = this.filtersGroup.valueChanges.pipe(
      startWith({min: 80, max: 120}),
      map((filters: Filters) => {
        const { min, max } = filters;
        let items = this.items;
        this.rangeMin = min;
        this.rangeMax = max;
        if (min && max) {
          items = items.filter((item) => item['price'] >= min && item['price'] <= max);
          return items;
        }

        if (min) {
          items = items.filter((item) => item['price'] >= min);
          return items;
        }

        if (max) {
          items = items.filter((item) => item['price'] <= max);
          return items;
        }

        return items;
      })
    );
  }

  clearFilters() {
    this.filtersGroup.reset();
  }
}

Buttons example

...
Black Jeans Jacket

100$

Buy now
...
Black Jeans Jacket

100$

Buy now
...
Gray Jumper

80$

Buy now
...
Gray Jumper

80$

Buy now
...
Red Hoodie

120$

Buy now
...
Blue Jeans Jacket

90$

Buy now
<div class="container">
  <form [formGroup]="filtersGroup">
    <div class="col-md-6">
      <div class="form-check mt-3">
        <button
          type="button"
          class="btn btn-primary"
          (click)="filtersGroup.get('sort')?.setValue('asc')"
        >
          Sort by product name ascending
        </button>
      </div>

      <div class="form-check">
        <button
          type="button"
          class="btn btn-primary"
          (click)="filtersGroup.get('sort')?.setValue('desc')"
        >
          Sort by product name descending
        </button>
      </div>
    </div>
  </form>

  <div class="row text-center">
    <div *ngFor="let item of filteredItems$ | async" class="col-md-4 mt-3"  style="max-width: 350px;">
      <div class="card shadow-2">
        <img [attr.src]="item.img" class="card-img-top" alt="..." />
        <div class="card-body">
          <h5 class="card-title">{{ item.product }}</h5>
          <p class="card-text">{{ item.price }} $</p>
          <a href="#" class="btn btn-primary ripple-surface">Buy now</a>
        </div>
      </div>
    </div>
  </div>
</div>
import { Component } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';

export interface Item {
  id: number;
  color: string;
  price: number;
  sale: string;
  product: string;
  img: string;
  size: number[];
}

export interface Filters {
  sort: string | null;
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {
  items: Item[] = [
    {
      id: 1,
      color: 'black',
      price: 100,
      sale: 'no',
      product: 'Black Jeans Jacket',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/15.webp',
      size: [36, 40, 42],
    },
    {
      id: 2,
      color: 'black',
      price: 100,
      sale: 'no',
      product: 'Black Jeans Jacket',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/15.webp',
      size: [36, 38, 40, 42],
    },
    {
      id: 3,
      color: 'gray',
      price: 80,
      sale: 'yes',
      product: 'Gray Jumper',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/14.webp',
      size: [36, 38, 40],
    },
    {
      id: 4,
      color: 'gray',
      price: 80,
      sale: 'yes',
      product: 'Gray Jumper',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/14.webp',
      size: [40, 42],
    },
    {
      id: 5,
      color: 'red',
      price: 120,
      sale: 'yes',
      product: 'Red Hoodie',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/13.webp',
      size: [36, 40, 42],
    },
    {
      id: 6,
      color: 'blue',
      price: 90,
      sale: 'no',
      product: 'Blue Jeans Jacket',
      img: 'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/12.webp',
      size: [36, 38],
    },
  ];

  filteredItems$: Observable<Item[]>;
  filtersGroup: FormGroup;

  basicButtonFilters: Filters = {
    sort: null,
  };

  constructor() {
    this.filtersGroup = new FormGroup({
      sort: new FormControl(),
    });

    this.filteredItems$ = this.filtersGroup.valueChanges.pipe(
      startWith(this.basicButtonFilters),
      map((filters: Filters) => {
        const { sort } = filters;
        let items = this.items;

        if (sort) {
          items = items.sort((a, b) =>
            sort === 'asc'
              ? a['product'].localeCompare(b['product'])
              : -1 * a['product'].localeCompare(b['product'])
          );
        }

        return items;
      })
    );
  }

  clearFilters() {
    this.filtersGroup.reset();
  }
}

Full page example

<header>
  <!-- Navbar -->
  <nav class="navbar sticky-top navbar-expand-lg navbar-light bg-white">
    <!-- Container wrapper -->
    <div class="container-fluid">
      <!-- Navbar brand -->

      <!-- Toggle button -->
      <button
        class="navbar-toggler"
        type="button"
        (click)="buttonsNav.toggle()"
        aria-expanded="false"
        aria-label="Toggle navigation"
      >
        <i class="fas fa-bars"></i>
      </button>

      <!-- Collapsible wrapper -->
      <div
        class="collapse navbar-collapse"
        id="navbarButtonsExample"
        mdbCollapse
        #buttonsNav="mdbCollapse"
      >
        <!-- Left links -->
        <ul class="navbar-nav me-auto mb-2 mb-lg-0">
          <a class="navbar-brand" href="https://mdbecommerce.com/">
            <i class="fab fa-mdb fa-2x" alt="mdb logo"></i>
          </a>
        </ul>
        <!-- Right links -->

        <ul class="navbar-nav ml-auto">
          <li class="nav-item my-auto">
            <a class="nav-link active" aria-current="page" href="#"
              ><span class="badge rounded-pill bg-danger">1</span
              ><i class="fas fa-shopping-cart"></i
            ></a>
          </li>
          <!-- Navbar dropdown -->
          <div mdbDropdown class="dropdown my-auto">
            <a
              class="dropdown-toggle text-black"
              role="button"
              id="dropdownMenuLink"
              mdbDropdownToggle
              aria-expanded="false"
            >
              <i class="united kingdom flag m-0"></i>
            </a>
            <ul
              mdbDropdownMenu
              class="dropdown-menu"
              aria-labelledby="dropdownMenuLink"
            >
              <li><a class="dropdown-item" href="#">Action</a></li>
              <li><a class="dropdown-item" href="#">Another action</a></li>
              <li><a class="dropdown-item" href="#">Something else here</a></li>
            </ul>
          </div>

          <li class="nav-item my-auto">
            <a class="nav-link active" aria-current="page" href="#">Shop</a>
          </li>
          <li class="nav-item my-auto">
            <a class="nav-link active" aria-current="page" href="#">Contact</a>
          </li>
          <li class="nav-item my-auto">
            <a class="nav-link active" aria-current="page" href="#">Sign in</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="#"
              ><button
                type="button"
                class="btn btn-outline-dark btn-rounded ripple-surface ripple-surface-dark"
                data-ripple-color="dark"
              >
                Sign up
              </button></a
            >
          </li>
        </ul>
      </div>
      <!-- Collapsible wrapper -->
    </div>
    <!-- Container wrapper -->
  </nav>
  <!-- Navbar -->
  <div
    class="p-5 text-center bg-image"
    style="
      background-image: url('https://mdbootstrap.com/img/Photos/Others/clothes(5)-crop.jpg');
      height: 400px;
    "
  >
    <div class="mask" style="background-color: rgba(0, 0, 0, 0.7)">
      <div class="d-flex justify-content-center align-items-center h-100">
        <div class="text-white">
          <h1 class="mb-3">Shop</h1>
        </div>
      </div>
    </div>
  </div>
</header>
<main>
  <div class="container mt-5" [formGroup]="filtersGroup">
    <div class="row">
      <div class="col-md-4">
        <!-- Section: Sidebar -->
        <section>
          <!-- Section: Filters -->
          <section id="filters" data-auto-filter="true">
            <h5>Filters</h5>

            <!-- Section: Condition -->
            <section class="mb-4">
              <h6 class="font-weight-bold mb-3">Condition</h6>

              <div class="form-check mb-3">
                <input
                  mdbCheckbox
                  class="form-check-input"
                  type="checkbox"
                  value="new"
                  id="checkbox1"
                  formControlName="conditionNew"
                />
                <label class="form-check-label" for="checkbox1"> NEW </label>
              </div>

              <div class="form-check mb-3">
                <input
                  mdbCheckbox
                  class="form-check-input"
                  type="checkbox"
                  value="used"
                  id="checkbox2"
                  formControlName="conditionUsed"
                />
                <label class="form-check-label" for="checkbox2"> USED </label>
              </div>
              <div class="form-check mb-3">
                <input
                  mdbCheckbox
                  class="form-check-input"
                  type="checkbox"
                  value="collectible"
                  id="checkbox3"
                  formControlName="conditionCollectible"
                />
                <label class="form-check-label" for="checkbox3">
                  COLLECTIBLE
                </label>
              </div>

              <div class="form-check mb-3">
                <input
                  mdbCheckbox
                  class="form-check-input"
                  type="checkbox"
                  value="renewed"
                  id="checkbox4"
                  formControlName="conditionRenewed"
                />
                <label class="form-check-label" for="checkbox4">
                  RENEWED
                </label>
              </div>
            </section>
            <!-- Section: Condition -->

            <!-- Section: Avg. Customer Review -->
            <section class="mb-4">
              <h6 class="font-weight-bold mb-3">Avg. Customer Review</h6>

              <mdb-rating
                (onSelect)="filtersGroup.get('rating')?.setValue($event)"
                style="cursor: pointer"
              >
                <mdb-rating-icon
                  mdbTooltip="1"
                  class="text-primary"
                ></mdb-rating-icon>
                <mdb-rating-icon
                  mdbTooltip="2"
                  class="text-primary"
                ></mdb-rating-icon>
                <mdb-rating-icon
                  mdbTooltip="3"
                  class="text-primary"
                ></mdb-rating-icon>
                <mdb-rating-icon
                  mdbTooltip="4"
                  class="text-primary"
                ></mdb-rating-icon>
                <mdb-rating-icon
                  mdbTooltip="5"
                  class="text-primary"
                ></mdb-rating-icon>
              </mdb-rating>
            </section>
            <!-- Section: Avg. Customer Review -->

            <!-- Section: Price -->
            <section class="mb-4">
              <h6 class="font-weight-bold mb-3">Price</h6>
              <div class="form-check mb-3">
                <input
                  mdbRadio
                  class="form-check-input"
                  type="radio"
                  name="price"
                  id="radio1"
                  formControlName="price"
                  value="<25"
                />
                <label class="form-check-label" for="radio1"> Under $25 </label>
              </div>

              <div class="form-check mb-3">
                <input
                  mdbRadio
                  class="form-check-input"
                  type="radio"
                  name="price"
                  id="radio2"
                  formControlName="price"
                  value="25-50"
                />
                <label class="form-check-label" for="radio2">
                  $25 to $50
                </label>
              </div>

              <div class="form-check mb-3">
                <input
                  mdbRadio
                  class="form-check-input"
                  type="radio"
                  name="price"
                  id="radio3"
                  formControlName="price"
                  value="50-100"
                />
                <label class="form-check-label" for="radio3">
                  $50 to $100
                </label>
              </div>

              <div class="form-check mb-3">
                <input
                  mdbRadio
                  class="form-check-input"
                  type="radio"
                  name="price"
                  id="radio4"
                  formControlName="price"
                  value="100-200"
                />
                <label class="form-check-label" for="radio4">
                  $100 to $200
                </label>
              </div>
              <div class="form-check mb-3">
                <input
                  mdbRadio
                  class="form-check-input"
                  type="radio"
                  name="price"
                  id="radio5"
                  formControlName="price"
                  value=">200"
                />
                <label class="form-check-label" for="radio5">
                  $200 & above
                </label>
              </div>
            </section>
            <!-- Section: Price -->

            <!-- Section: Size -->
            <section class="mb-4" data-filter="size">
              <h6 class="font-weight-bold mb-3">Size</h6>

              <div class="form-check mb-3">
                <input
                  mdbCheckbox
                  class="form-check-input"
                  type="checkbox"
                  value="34"
                  id="checkbox5"
                  formControlName="size34"
                />
                <label class="form-check-label" for="checkbox5"> 34 </label>
              </div>

              <div class="form-check mb-3">
                <input
                  mdbCheckbox
                  class="form-check-input"
                  type="checkbox"
                  value="36"
                  id="checkbox6"
                  formControlName="size36"
                />
                <label class="form-check-label" for="checkbox6"> 36 </label>
              </div>
              <div class="form-check mb-3">
                <input
                  mdbCheckbox
                  class="form-check-input"
                  type="checkbox"
                  value="38"
                  id="checkbox7"
                  formControlName="size38"
                />
                <label class="form-check-label" for="checkbox7"> 38 </label>
              </div>

              <div class="form-check mb-3">
                <input
                  mdbCheckbox
                  class="form-check-input"
                  type="checkbox"
                  value="40"
                  id="checkbox8"
                  formControlName="size40"
                />
                <label class="form-check-label" for="checkbox8"> 40 </label>
              </div>
            </section>
            <!-- Section: Size -->

            <!-- Section: Color -->
            <section class="mb-4" data-filter="color">
              <h6 class="font-weight-bold mb-3">Color</h6>

              <div class="form-check form-check-inline mx-0 px-0 py-3">
                <input
                  formControlName="color"
                  mdbRadio
                  class="btn-check"
                  type="radio"
                  name="color"
                  id="colorRadio1"
                  value="white"
                />
                <label
                  class="btn bg-light btn-rounded p-3"
                  for="colorRadio1"
                ></label>
              </div>

              <div class="form-check form-check-inline mx-0">
                <input
                  formControlName="color"
                  mdbRadio
                  class="btn-check"
                  type="radio"
                  name="color"
                  id="colorRadio2"
                  value="grey"
                />
                <label
                  class="btn btn-rounded p-3"
                  for="colorRadio2"
                  style="background-color: #bdbdbd"
                ></label>
              </div>

              <div class="form-check form-check-inline mx-0">
                <input
                  formControlName="color"
                  mdbRadio
                  class="btn-check"
                  type="radio"
                  name="color"
                  id="colorRadio3"
                  value="black"
                />
                <label
                  class="btn bg-dark text-white btn-rounded p-3"
                  for="colorRadio3"
                ></label>
              </div>

              <div class="form-check form-check-inline mx-0">
                <input
                  formControlName="color"
                  mdbRadio
                  class="btn-check"
                  type="radio"
                  name="color"
                  id="colorRadio5"
                  value="blue"
                />
                <label
                  class="btn bg-primary btn-rounded p-3"
                  for="colorRadio5"
                ></label>
              </div>

              <div class="form-check form-check-inline mx-0">
                <input
                  formControlName="color"
                  mdbRadio
                  class="btn-check"
                  type="radio"
                  name="color"
                  id="colorRadio9"
                  value="red"
                />
                <label
                  class="btn bg-danger btn-rounded p-3"
                  for="colorRadio9"
                ></label>
              </div>

              <div class="form-check form-check-inline mx-0">
                <input
                  formControlName="color"
                  mdbRadio
                  class="btn-check"
                  type="radio"
                  name="color"
                  id="colorRadio10"
                  value="orange"
                />
                <label
                  class="btn bg-warning btn-rounded p-3"
                  for="colorRadio10"
                ></label>
              </div>
            </section>
            <!-- Section: Color -->
          </section>
          <!-- Section: Filters -->
        </section>
        <!-- Section: Sidebar -->
      </div>
      <div class="col-md-8">
        <div class="row justify-content-center">
          <div class="col-md-6 my-auto py-3">
            <mdb-form-control>
              <mdb-select formControlName="sort">
                <mdb-option
                  *ngFor="let option of options"
                  [value]="option.value"
                  >{{ option.label }}</mdb-option
                >
              </mdb-select>
              <label mdbLabel class="form-label">Sort</label>
            </mdb-form-control>
          </div>
        </div>
        <div class="row mb-4" id="content" style="display: flex">
          <div class="row text-center">
            <h3 *ngIf="noItemsFound" class="text-center mt-5">
              No items found
            </h3>
            <div
              *ngFor="let item of filteredItems$ | async"
              class="col-md-4 mt-3"
              style="max-width: 350px"
            >
              <div class="card shadow-2">
                <img [attr.src]="item.image" class="card-img-top" alt="..." />
                <div class="card-body">
                  <h5 class="card-title">{{ item.name }}</h5>
                  <p class="card-text">{{ item.price }} $</p>
                  <a href="#" class="btn btn-primary ripple-surface">Buy now</a>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</main>
<footer class="bg-dark text-white text-center text-lg-left">
  <!-- Socials -->
  <div class="bg-primary text-center p-3">
    <div class="row">
      <div class="col-md-6">
        <span class="font-weight-bold"
          >Get connected with us on social networks!</span
        >
      </div>
      <div class="col-md-6">
        <i class="fab fa-instagram"></i>
        <i class="fab fa-linkedin-in ms-4"></i>
        <i class="fab fa-twitter ms-4"></i>
        <i class="fab fa-facebook-f ms-4"></i>
      </div>
    </div>
  </div>
  <!-- Socials -->

  <!-- Grid container -->
  <div class="container p-5">
    <!--Grid row-->
    <div class="row p-2">
      <!--Grid column-->
      <div class="col-md-3 mx-auto py-4 text-start">
        <h5 class="text-uppercase">About me</h5>
        <hr class="mb-4 mt-0" />

        <p>
          Here you can use rows and columns to organize your footer content.
          Lorem ipsum dolor sit amet, consectetur adipisicing elit.
        </p>
      </div>
      <!--Grid column-->

      <!--Grid column-->
      <div class="col-md-3 mx-auto py-4">
        <h5 class="text-uppercase text-start">Products</h5>
        <hr class="mb-4 mt-0" />

        <ul class="list-unstyled mb-0 text-start">
          <li class="mb-2">
            <a href="#!" class="text-white">MDBootstrap</a>
          </li>
          <li class="mb-2">
            <a href="#!" class="text-white">MDWordPress</a>
          </li>
          <li class="mb-2">
            <a href="#!" class="text-white">BrandFlow</a>
          </li>
          <li>
            <a href="#!" class="text-white">Bootstrap Angular</a>
          </li>
        </ul>
      </div>
      <!--Grid column-->

      <!--Grid column-->
      <div class="col-md-3 mx-auto py-4">
        <h5 class="text-uppercase text-start">Useful links</h5>
        <hr class="mb-4 mt-0" />

        <ul class="list-unstyled text-start">
          <li class="mb-2">
            <a href="#!" class="text-white">Your Account</a>
          </li>
          <li class="mb-2">
            <a href="#!" class="text-white">Become an Affiliate</a>
          </li>
          <li class="mb-2">
            <a href="#!" class="text-white">Shipping Rates</a>
          </li>
          <li>
            <a href="#!" class="text-white">Help</a>
          </li>
        </ul>
      </div>
      <!--Grid column-->

      <!--Grid column-->
      <div class="col-md-3 mx-auto py-4">
        <h5 class="text-uppercase text-start">Contacts</h5>
        <hr class="mb-4 mt-0" />

        <ul class="list-unstyled text-start">
          <li class="mb-2">
            <a href="#!" class="text-white"
              ><i class="far fa-map mr-1"></i> New York, Avenue Street 10</a
            >
          </li>
          <li class="mb-2">
            <a href="#!" class="text-white"
              ><i class="fas fa-phone-alt mr-1"></i> 042 876 836 908</a
            >
          </li>
          <li class="mb-2">
            <a href="#!" class="text-white"
              ><i class="far fa-envelope mr-1"></i> company@example.com</a
            >
          </li>
          <li>
            <a href="#!" class="text-white"
              ><i class="far fa-clock mr-1"></i> Monday - Friday: 10-17</a
            >
          </li>
        </ul>
      </div>
      <!--Grid column-->
    </div>
    <!--Grid row-->
  </div>
  <!-- Grid container -->

  <!-- Copyright -->
  <div class="text-center p-3" style="background-color: rgba(0, 0, 0, 0.2)">
    © 2020 Copyright:
    <a class="text-white" href="https://mdbootstrap.com/">MDBootstrap.com</a>
  </div>
  <!-- Copyright -->
</footer>
import { Component } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';

export interface Item {
  id: number;
  category: string;
  name: string;
  rating: number;
  image: string;
  description: string;
  available: boolean;
  size: string[] | number[];
  condition: string;
  color: string;
  price: number;
  discount: number;
  gender: string;
  keywords?: string[];
}

export interface Filters {
  sale: string | null;
  sort: string | null;
  color: string | null;
  conditionNew: boolean | null;
  conditionUsed: boolean | null;
  conditionCollectible: boolean | null;
  conditionRenewed: boolean | null;
  rating: number | null;
  price: number | null;
  size34: number | null;
  size36: number | null;
  size38: number | null;
  size40: number | null;
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {
  filteredItems$: Observable<Item[]>;
  filtersGroup: FormGroup;

  defaultFilters: Filters = {
    color: null,
    sale: null,
    sort: null,
    conditionNew: null,
    conditionUsed: null,
    conditionCollectible: null,
    conditionRenewed: null,
    rating: null,
    price: null,
    size34: null,
    size36: null,
    size38: null,
    size40: null,
  };

  options = [
    { value: 'ratingdesc', label: 'Best rating' },
    { value: 'pricedesc', label: 'Higest price first' },
    { value: 'priceasc', label: 'Lowest price first' },
  ];

  noItemsFound = false;

  items: Item[] = [
    {
      id: 1,
      category: 'shirts',
      name: 'Fantasy T-shirt',
      rating: 4,
      image:
        'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/12.webp',
      description:
        'Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.',
      available: true,
      size: ['34', '36', '40'],
      condition: 'new',
      color: 'blue',
      price: 12.99,
      keywords: ['t-shirt', 'sweatshitrt'],
      discount: 0,
      gender: 'male',
    },
    {
      id: 2,
      category: 'shirts',
      name: 'Fantasy T-shirt',
      rating: 5,
      image:
        'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/13.webp',
      description:
        'Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.',
      available: true,
      size: ['34', '36', '40', '44'],
      condition: 'new',
      color: 'red',
      price: 12.99,
      discount: 0,
      gender: 'male',
    },
    {
      id: 3,
      category: 'shirts',
      name: 'Fantasy T-shirt',
      rating: 3,
      image:
        'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/14.webp',
      description:
        'Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.',
      available: true,
      size: ['34', '36'],
      condition: 'new',
      color: 'grey',
      price: 40.99,
      discount: 10,
      gender: 'male',
    },
    {
      id: 4,
      category: 'jackets',
      name: 'Denim Jacket',
      rating: 5,
      size: [],
      image:
        'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/15.webp',
      description:
        'Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.',
      available: true,
      condition: 'new',
      color: 'grey',
      price: 40.99,
      discount: 0,
      gender: 'unisex',
    },
    {
      id: 5,
      category: 'jeans',
      name: 'Ripped jeans',
      rating: 5,
      image:
        'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/11.webp',
      description:
        'Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.',
      available: true,
      size: ['34', '36', '38', '40'],
      condition: 'renewed',
      color: 'blue',
      price: 20.99,
      discount: 5,
      gender: 'female',
    },
    {
      id: 6,
      category: 'jeans',
      name: 'Boyfriend jeans',
      rating: 4,
      image:
        'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/10.webp',
      description:
        'Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.',
      available: false,
      size: ['34', '36', '38', '40'],
      condition: 'used',
      color: 'blue',
      price: 20.99,
      discount: 5,
      gender: 'female',
    },
    {
      id: 7,
      category: 'shirts',
      name: 'Ripped sweatshirt',
      rating: 4,
      image:
        'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/7.webp',
      description:
        'Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.',
      available: true,
      size: ['34', '36', '38', '40'],
      condition: 'collectible',
      color: 'white',
      price: 29.99,
      discount: 5,
      gender: 'female',
    },
    {
      id: 8,
      category: 'shirts',
      name: 'Longsleeve',
      rating: 4,
      image:
        'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/8.webp',
      description:
        'Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.',
      available: true,
      size: ['40'],
      condition: 'collectible',
      color: 'black',
      price: 120.99,
      discount: 0,
      gender: 'male',
    },
    {
      id: 8,
      category: 'shirts',
      name: 'Stripped sweatshirt',
      rating: 4,
      image:
        'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/6.webp',
      description:
        'Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.',
      available: true,
      size: ['40', '38', '36'],
      condition: 'new',
      color: 'white',
      price: 32.99,
      discount: 10,
      gender: 'female',
    },
    {
      id: 9,
      category: 'trousers',
      name: 'Red chinos',
      rating: 5,
      image:
        'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/5.webp',
      description:
        'Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.',
      available: true,
      size: ['40', '38', '36'],
      condition: 'new',
      color: 'red',
      price: 62.99,
      discount: 10,
      gender: 'female',
    },
    {
      id: 10,
      category: 'coats',
      name: 'Camel coat',
      rating: 5,
      image:
        'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/4.webp',
      description:
        'Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.',
      available: true,
      size: ['40', '38', '36'],
      condition: 'used',
      color: 'brown',
      price: 62.99,
      discount: 10,
      gender: 'female',
    },
    {
      id: 11,
      category: 'jeans',
      name: 'Blue jeans',
      rating: 5,
      image:
        'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/3.webp',
      description:
        'Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.',
      available: true,
      size: ['40', '38', '36'],
      condition: 'new',
      color: 'blue',
      price: 42.99,
      discount: 0,
      gender: 'female',
    },
    {
      id: 12,
      category: 'shirts',
      name: 'Orange T-shirt',
      rating: 3,
      image:
        'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/3.webp',
      description:
        'Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.',
      available: true,
      size: ['40', '38', '36'],
      condition: 'new',
      color: 'orange',
      price: 12.99,
      discount: 0,
      gender: 'female',
    },
    {
      id: 13,
      category: 'skirts',
      name: 'Ballerina skirt',
      rating: 4,
      image:
        'https://mdbcdn.b-cdn.net/img/Photos/Horizontal/E-commerce/Vertical/2.webp',
      description:
        'Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam, sapiente illo. Sit error voluptas repellat rerum quidem, soluta enim perferendis voluptates laboriosam.',
      available: true,
      size: ['38', '36'],
      condition: 'collectible',
      color: 'white',
      price: 12.99,
      discount: 0,
      gender: 'female',
    },
  ];

  constructor() {
    this.filtersGroup = new FormGroup({
      color: new FormControl(),
      sale: new FormControl(),
      conditionNew: new FormControl(),
      conditionUsed: new FormControl(),
      conditionCollectible: new FormControl(),
      conditionRenewed: new FormControl(),
      rating: new FormControl(),
      price: new FormControl(),
      size34: new FormControl(),
      size36: new FormControl(),
      size38: new FormControl(),
      size40: new FormControl(),
      sort: new FormControl()
    });

    this.filteredItems$ = this.filtersGroup.valueChanges.pipe(
      startWith(this.defaultFilters),
      map((controls: Filters) => {
        const { rating, price, color, sort } = controls;
        let items = this.items;
        const conditions = Object.keys(controls)
          .filter(
            (control) =>
              controls[control as keyof Filters] &&
              control.startsWith('condition')
          )
          .map((string) => string.toLowerCase().replace('condition', ''));

        if (conditions.length > 0) {
          items = items.filter((item) =>
          conditions.some((condition) => item.condition === condition)
          );
        }

        if (rating) {
          items = items.filter((item) => item['rating'] === rating)
        }

        if (price) {
          if (price.toString().startsWith('<')) {
            items = items.filter((item) => item['price'] < Number(price.toString().split('<')[1]));
          }
          if (price.toString().startsWith('>')) {
            items = items.filter((item) => item['price'] > Number(price.toString().split('>')[1]));
          }
          if (price.toString().split('-').length === 2) {
            items = items.filter((item) => item['price'] > Number(price.toString().split('-')[0]) && item['price'] < Number(price.toString().split('-')[1]))
          }

        }

        const sizes = Object.keys(controls)
        .filter(
          (control) =>
            controls[control as keyof Filters] &&
            control.startsWith('size')
        )
        .map((string) => Number(string.toLowerCase().replace('size', '')));
        this.noItemsFound = false;
        if (sizes.length > 0) {
          items = items.filter((item) =>
            sizes.some((checkboxSize) => item['size'].some((itemSize) => itemSize === checkboxSize.toString()))
          );
        }

        if (color) {
          console.log(color)
          items = items.filter((item) => item['color'] === color);
        }

        if (sort && sort.startsWith('price')) {
          items = items.sort((a, b) =>
            sort.endsWith('asc') ? a['price'] - b['price'] : b['price'] - a['price']
          );
        }

        if (sort && sort.startsWith('rating')) {
          items = items.sort((a, b) =>
            sort.endsWith('asc') ? a['rating'] - b['rating'] : b['rating'] - a['rating']
          );
        }

        if (items.length < 1) {
          this.noItemsFound = true;
        }
        return items;
      })
    );
  }

  clearFilters() {
    this.filtersGroup.reset();
  }
}

Filters - API


Import

import { ReactiveFormsModule } from '@angular/forms';
  …
  @NgModule ({
    ...
    imports: [ReactiveFormsModule],
    ...
  })