Search code examples
angularangular-pipe

Angular Pipe chaining on a element does not work


I have created a filter and sort pipes in Angular and chained it as below. But the pipe chaining is not working. If I use only a single pipe it works but if I use two pipes like below only the filter functionality works sort pipe does not work. Can somebody tell me what is the mistake that I am doing?

      <div class="grid-container">
        <div class="grid-item" *ngFor="let prod of (productsList.products | sort:sortProductSelected | filterOnPrice:priceRange )">
          <div class="card" style="width: 18rem;border: 0px;">
            <img src="{{prod.thumbnail}}" class="card-img-top" alt="...">
            <div class="card-body">
              <h5 class="card-title">{{prod.brand}}</h5>
              <p class="card-text">${{prod.price}}</p>
              <p class="card-text">{{prod.description}}</p>
              <a target="_blank" class="btn btn-primary" (click)="viewProduct(prod.id)">View Product</a>
            </div>
          </div>
        </div>
      </div>
    </div>
    

Sort pipe

export class SortPipe implements PipeTransform {
  transform(value: any[], ...args: any): any[] {
    if(args){
      switch(args[0]){
        case 'Price low to hight': 
          value.sort((a,b)=>{
            if(a.price<b.price) return  -1;
            else if(a.price>b.price) return 1;
          return 0;
          console.log("value");

          })
          break;
        case 'Price High to Low' :
          value.sort((a,b)=>{
            if(a.price>b.price) return  -1;
            else if(a.price<b.price) return 1;
          return 0;
          })
          break;
        case 'Ratings':
          value.sort((a,b)=>{
            if(a.rating<b.rating) return  -1;
            else if(a.rating>b.rating) return 1;
          return 0;
          })
          break;
        case 'Discounts' :
          value.sort((a,b)=>{
            if(a.discountPercentage<b.discountPercentage) return  -1;
            else if(a.discountPercentage>b.discountPercentage) return 1;
          return 0;
          })
          break;
        default: return value;
      }  
    }

    return value;
  }

}

Filter Pipe

export class FilterOnPricePipe implements PipeTransform {
  transform(value: any[], ...args: any): any[] {
    let FliteredArray:any[] =[];

    FliteredArray = value.filter((a)=>{
           console.log(a.price);
           console.log(FliteredArray);
      return a.price<=args[0];

    })
    console.log(FliteredArray);
    return FliteredArray;
  }

}

Solution

  • Its best to use the filterPipe first then perform the sortPipe when I did this it worked fine, since we are removing the unnecessary values and sorting it will improve the performance!

    Also I guess the input reference did not change so you were getting this issue!

    Because when I changed the line 45 in sortPipe it started working fine (without swapping the pipes), so I guess the reference of the array needs to be updated for the pipe to be triggered.

    return [...value];
    

    I think you should perform the filter first and then sort, since its makes sense and no need to update the reference!

    Below is a working example!

    ts

    import { CommonModule } from '@angular/common';
    import { Component } from '@angular/core';
    import { FormsModule } from '@angular/forms';
    import { bootstrapApplication } from '@angular/platform-browser';
    import 'zone.js';
    import { FilterOnPricePipe } from './filter.pipe';
    import { SortPipe } from './sort.pipe';
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports: [CommonModule, FilterOnPricePipe, SortPipe, FormsModule],
      template: `
      sortProductSelected
      <select [(ngModel)]="sortProductSelected">
            <option *ngFor="let state of states" [ngValue]="state.name">
              {{ state.name }}
            </option>
          </select><br/>
          <input type="number" [(ngModel)]="priceRange"/>
      <div class="grid-container">
         <div class="grid-item" *ngFor="let prod of (productsList.products | filterOnPrice:priceRange | sort:sortProductSelected )">
           <div class="card" style="width: 18rem;border: 0px;">
             <img src="{{prod.thumbnail}}" class="card-img-top" alt="..."/>
             <div class="card-body">
               <h5 class="card-title">{{prod.brand}}</h5>
               <p class="card-text">$ {{prod.price}}</p>
               <p class="card-text">{{prod.description}}</p>
               <a target="_blank" class="btn btn-primary" (click)="viewProduct(prod.id)">View Product</a>
             </div>
           </div>
         </div>
       </div>  
       `,
    })
    export class App {
      states = [
        { name: 'Price low to hight' },
        { name: 'Price High to Low' },
        { name: 'Ratings' },
        { name: 'Discounts' },
      ];
      sortProductSelected = 'Price low to hight';
      priceRange = 15;
      name = 'Angular';
      productsList = {
        products: [
          {
            thumbnail: 'https://placehold.co/600x400',
            brand: 'test',
            price: 1,
            rating: 1,
            discountPercentage: 1,
            description: 'test',
            id: 1,
          },
          {
            thumbnail: 'https://placehold.co/600x400',
            brand: 'test',
            price: 2,
            rating: 2,
            discountPercentage: 2,
            description: 'test',
            id: 2,
          },
          {
            thumbnail: 'https://placehold.co/600x400',
            brand: 'test',
            price: 3,
            rating: 3,
            discountPercentage: 3,
            description: 'test',
            id: 3,
          },
        ],
      };
    
      viewProduct(e: any) {}
    }
    
    bootstrapApplication(App);
    

    stackblitz