Understanding Angular Pipes

Understanding Angular Pipes

Question 1: What are Angular Pipes and what is their purpose?

Answer: Angular Pipes are a feature that allows you to transform and format data in your templates without changing the underlying data. They are used to:

  • Transform data display format
  • Perform data conversions
  • Apply filters
  • Format dates, numbers, and text
  • Create reusable data transformations

Question 2: What are the built-in pipes in Angular and how do you use them?

Answer: Angular provides several built-in pipes. Here are the most commonly used ones with examples:

@Component({
  selector: 'app-pipe-demo',
  template: `
    <!-- Date Pipe -->
    <p>Date: {{ today | date: 'fullDate' }}</p>
    <p>Custom Date: {{ today | date: 'dd/MM/yyyy HH:mm' }}</p>

    <!-- Currency Pipe -->
    <p>Price: {{ price | currency: 'USD' }}</p>
    <p>Custom Currency: {{ price | currency: 'EUR':'code' }}</p>

    <!-- Decimal Pipe -->
    <p>Number: {{ number | number: '1.2-2' }}</p>

    <!-- Uppercase/Lowercase Pipes -->
    <p>{{ text | uppercase }}</p>
    <p>{{ text | lowercase }}</p>

    <!-- JSON Pipe -->
    <pre>{{ object | json }}</pre>

    <!-- Async Pipe -->
    <div>{{ observableData$ | async }}</div>

    <!-- Slice Pipe -->
    <p>{{ array | slice:1:3 }}</p>

    <!-- Percent Pipe -->
    <p>{{ 0.756 | percent:'1.1-2' }}</p>
  `
})
export class PipeDemoComponent {
  today = new Date();
  price = 42.99;
  number = 3.14159;
  text = 'Angular Pipes';
  object = { name: 'John', age: 30 };
  array = [1, 2, 3, 4, 5];
  observableData$ = of('Async Data').pipe(delay(1000));
}

Question 3: How do you create a custom pipe in Angular?

Answer: Here’s how to create and use a custom pipe:

// filter-list.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'filterList',
  // Make it pure: false if you want it to update on all changes
  pure: true,
  // Available in standalone components
  standalone: true
})
export class FilterListPipe implements PipeTransform {
  transform(items: any[], searchText: string): any[] {
    if (!items || !searchText) {
      return items;
    }

    searchText = searchText.toLowerCase();
    
    return items.filter(item => 
      item.name.toLowerCase().includes(searchText)
    );
  }
}

// usage in component
@Component({
  selector: 'app-list',
  template: `
    <input [(ngModel)]="searchText" placeholder="Search...">
    <ul>
      @for (item of items | filterList:searchText; track item.id) {
        <li>{{ item.name }}</li>
      }
    </ul>
  `,
  // Import in standalone component
  imports: [FilterListPipe]
})
export class ListComponent {
  items = [
    { id: 1, name: 'Angular' },
    { id: 2, name: 'React' },
    { id: 3, name: 'Vue' }
  ];
  searchText = '';
}

Question 4: What is the difference between pure and impure pipes?

Answer: Pure and impure pipes differ in how they handle change detection:

// Pure Pipe (Default)
@Pipe({
  name: 'pure',
  pure: true // default
})
export class PurePipe implements PipeTransform {
  transform(value: any) {
    // Only executes when input value reference changes
    return value;
  }
}

// Impure Pipe
@Pipe({
  name: 'impure',
  pure: false
})
export class ImpurePipe implements PipeTransform {
  transform(value: any) {
    // Executes on every change detection cycle
    return value;
  }
}

Interview Tips 💡

  1. Performance Considerations

    • Pure pipes are more performant as they only run on reference changes
    • Use pure pipes by default unless you specifically need impure behavior
    • Be cautious with impure pipes as they can impact performance
  2. Built-in Pipes Best Practices

    // Chaining pipes
    {{ date | date:'shortDate' | uppercase }}
    
    // Using pipe parameters
    {{ number | number:'1.2-2' }}
    
    // Async pipe for automatic subscription handling
    {{ observable$ | async }}
  3. Common Use Cases

    • Date formatting
    • Number and currency formatting
    • Text transformation
    • Filtering lists
    • Handling async data
  4. Advanced Pipe Features

    // Parameterized pipe
    @Pipe({ name: 'exponential' })
    export class ExponentialPipe implements PipeTransform {
      transform(value: number, exponent = 1): number {
        return Math.pow(value, exponent);
      }
    }
    // Usage: {{ 2 | exponential:3 }} outputs 8
  5. Error Handling in Pipes

    @Pipe({ name: 'safe' })
    export class SafePipe implements PipeTransform {
      transform(value: any): any {
        try {
          // Transform logic
          return processedValue;
        } catch (error) {
          console.error('Pipe error:', error);
          return value; // Return original value on error
        }
      }
    }
  6. Testing Pipes

    describe('FilterListPipe', () => {
      const pipe = new FilterListPipe();
      
      it('should filter list by search text', () => {
        const items = [
          { name: 'Angular' },
          { name: 'React' }
        ];
        
        const filtered = pipe.transform(items, 'ang');
        expect(filtered.length).toBe(1);
        expect(filtered[0].name).toBe('Angular');
      });
    });

Remember: In interviews, focus on:

  • Understanding when to use pipes vs methods
  • Performance implications of pure vs impure pipes
  • Best practices for creating custom pipes
  • Knowledge of built-in pipes and their parameters
  • Async pipe benefits for memory management
  • Testing strategies for pipes
  • Error handling and edge cases