kamil
kamil

Reputation: 81

Angular filtering items in array by 2 criteria

I created sorting sidebar which filter my product list based on category and price. Filtering by category works correctly. sidebar I have a problem with filtering based on the price of the product.If I set the category to "Smartfons" and filter a product with a price higher than 2000, it will return Iphone X correctly in my case:Price more than 2000 but when I change the filter to "All" I have this one phone and It should return 3 phones:All smartfons

    export class ProductComponent implements OnInit {
  filteredProduct: Products[] = [];
  products: Products[] = [];
  currentSorting: string;
  wrapper = true;
  @ViewChild('filtersComponent')
  filtersComponent: SortProductsComponent;

  constructor(protected productsService: CategoriesProductsService) { }

    sortFilters: any[] = [
      { name: 'Name (A to Z)', value: 'name' },
      { name: 'Price (low to high)', value: 'priceAsc' },
      { name: 'Price (high to low)', value: 'priceDes' }
    ];
    priceFilters: any[] = [
      { name: 'All', value: 'all', checked: true },
      { name: 'Price > 2000', value: 'more_2000', checked: false },
      { name: 'Price < 500', value: 'less_500', checked: false }
    ];

  ngOnInit() {
    this.displayProducts();
  }
  displayProducts() {
    this.productsService.getProducts().subscribe(product => {
      this.products = product;
      this.filteredProduct = product; });
    }

    onFilterChange(data) {
      if (data.type === 'category') {
      if (data.isChecked) {
          // tslint:disable-next-line:prefer-for-of
          for (let i = 0; i < data.filter.Products.length; i++) {
          this.filteredProduct.push(data.filter.Products[i]);
          }
        } else {
          this.filteredProduct =
          this.products.filter(x => {
            return x.CategoryId !== data.filter.Id; } );
        }
      } else if (data.type === 'price') {
        this.filteredProduct = this.products;
        if (data.isChecked) {
          const priceFilter = data.filter.value;
          if (priceFilter === 'all') {
              this.filteredProduct = this.products;
          } else if (priceFilter === 'more_2000' ) {

              this.filteredProduct = this.products.filter(x => x.Price > 2000);

          } else if (priceFilter === 'less_500' ) {

                this.filteredProduct = this.products.filter(x => x.Price < 500);
          }
        }
      }
    }

SortProductComponent:

   export class SortProductsComponent implements OnInit {
  categoriesList: Categories[];
  @Input()
  priceFilters: any[];
  // tslint:disable-next-line:no-output-on-prefix
  @Output()
  onFilterChange = new EventEmitter<any>();
  showFilters = true;
  sideShown = false;
  constructor(private categoriesService: CategoriesProductsService) { }

  ngOnInit() {
    this.displayCategories();
  }
  displayCategories() {
    this.categoriesService.getCategories().subscribe((data) => {
     this.categoriesList = data;

    });
  }
  onInputChange($event, filter, type) {
    const change = $event.target.checked ? 1 : -1;
    this.onFilterChange.emit({
      type,
      filter,
      isChecked: $event.target.checked,
      change
    });
}
}

HTML template:

   <h5>Filter by categories</h5>
  <form >
      <div class="category-filter filter-wrapper" *ngFor = 'let filter of categoriesList'>
        <div class="custom-control custom-checkbox">
            <label class="fake-checkbox">
              <input type="checkbox" class="custom-control-input" checked (change)='onInputChange($event, filter, "category")'>
              <span class="custom-control-label">  {{filter.Name}}</span>
              <span></span>
            </label>
        </div>
    </div>
</form>
<h5>Filter by price</h5>
<form *ngIf = "showFilters">
    <div class="custom-filter filter-wrapper" *ngFor = 'let filter of priceFilters'>
        <label class="fake-checkbox">
          <input type="radio" name="price" [checked]='filter.checked' (click)='onInputChange($event, filter, "price")'>
          <span class="circle"><span class="fill"></span></span>
          <span class="label">{{filter.name}}</span>
          <span></span>
        </label>
    </div>
  </form>

Product class:

export class Products {
  Id: number;
  Name: string;
  Description: string;
  DetailedDescription: string;
  Price: number;
  IsNewProduct: boolean;
  PromotionalProduct: boolean;
  Image: string;
  CategoryId: number;
}

I think that by using the filter () method, which returns a new array to me, the filter on the array works only once.

I would like my category and price filter to work just like on this page: https://carlosroso.com/angular2-shop

Any help is welcome

Upvotes: 0

Views: 4353

Answers (1)

DeborahK
DeborahK

Reputation: 60626

Yes, unless you want to re-get the products every time the user changes the filter, you'll need to retain the list of original products (unfiltered) and use it to filter your list.

Here is what my code looks like:

  ngOnInit(): void {
    this.productService.getProducts().subscribe(
      products => {
        this.products = products;
        this.filteredProducts = this.products;
      },
      error => this.errorMessage = <any>error
    );
  }

Notice that it is retaining the returned values from the service both in products, which is the complete list and filteredProducts which will always be the filtered list. (Though it is originally the full list of products assuming the display starts with no filters.)

Then the source of the filter is always the original list:

  performFilter(filterBy: string): IProduct[] {
    filterBy = filterBy.toLocaleLowerCase();
    return this.products.filter((product: IProduct) =>
      product.productName.toLocaleLowerCase().indexOf(filterBy) !== -1);
  }

Notice that it uses this.products.filter to ensure it always starts filtering from the full list of products.

Yours would looks something more like:

      this.filteredProduct = this.products.filter(x => {
        return x.CategoryId !== data.filter.Id; } );
    }

Upvotes: 1

Related Questions