Angular Pipes & custom pipes

Master Angular Pipes: Custom & Standard Pipes Access Data in a Whole New Way!
Master Angular Pipes: Introducing Data Using Built-in and Custom Pipes

When you finish, you will:

✅ Have an understanding of how pipes categorize data handling.
✅ Be able to use Angular's built-in pipes for date and money as well.
✅ Know how easy it is to fashion a custom pipe.
✅ Avoid common mistakes and follow best practices.

Turn dirty data into something that users love!

The Importance of Angular Pipes

Pipes offer a kind of form object that you can put in a template in front of the user directly. They're essential because:

  • Clean Code: Keep formatting out of containers.
  • Usability: Mix and match types.
  • Human-Friendly Interface: Style dates, currencies, and other text in a way that people can comprehend.

What Are Angular Pipes?

Pipes are functions that take in a variable and return a new value after processing. In a template, they are attached to the variable by the vertical line (|) followed by the pipe name:

{{ rawData | pipeName }}

Built-in Pipes: The Data Swiss Army Knife

Angular provides more than 20 convenient pipes. Here is a selection of common ones:

Pipe Example
DatePipe `{{ today | date:'short' }}`
CurrencyPipe `{{ price | currency:'USD' }}`
UpperCasePipe `{{ 'hello' | uppercase }}`
JsonPipe `{{ user | json }}`

Creating a Custom Pipe in 4 Steps

We want to create TruncatePipe for when we have long text and need to present it with an ellipsis (e.g., "Lorem ipsum...").

pipes and custom pipes in angular

Step 1: Generate the Pipe

ng generate pipe truncate

This creates truncate.pipe.ts and a test file.

Step 2: Define the Logic

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({ name: 'truncate' })
export class TruncatePipe implements PipeTransform {
  transform(value: string, maxLength: number = 50, ellipsis: string = '...'): string {
    if (!value) return '';
    return value.length > maxLength ? value.slice(0, maxLength) + ellipsis : value;
  }
}

Step 3: Declare the Pipe

Add to your module's declarations (or use standalone: true in Angular 15+):

@NgModule({
  declarations: [TruncatePipe],
})
export class AppModule {}

Step 4: Use It in a Template

{{ longText | truncate: 30 : "…" }}

Input: "With Angular pipes, data formatting is a piece of cake!"
Output: "With Angular pipes, data form…"

5 Real-World Custom Pipe Ideas

  1. Highlight Matches in Search Results: Develop a pipe that can transparently highlight matching text in a search.
  2. File Size Formatter: Change bytes into KB, MB, or GB.
  3. Emoji Converter: Switch :smile: into 😃.
  4. Password Mask: Cover raw text with ••••• so a user can’t read it.
  5. Temperature Converter: Convert between °C and °F.

Angular Pipe Best Practices

  1. Keep Pipes Pure: Avoid side effects (unless you really need to use impure pipes).
  2. Parameterize: Design pipes so they can be customized through input (like adding maxLength to TruncatePipe).
  3. Optimize Performance: Pure pipes aren't unexpectedly invoked.
  4. Test Thoroughly: Edge cases should be covered by Angular's own testing procedure.
  5. Use with Async Data: Pair the async pipe with Observables/Promises
{{ data$ | async | uppercase }}

Common Pitfalls to Avoid

🚫 Overuse of Impure Pipes: They’re called with each template change detection, which can hurt performance.
🚫 Ignoring Localization: For more information, link CurrencyPipe with global settings for your app.
🚫 Leaving out Error Handling: Check that custom pipes are not null/undefined.

Conclusion: Add Pipes to Your Angular Apps

Pipes are Angular's secret weapon for clear, reusable data formatting. Whether you're formatting dates or creating custom logic, your users will thank you when they find clean templates.

Your Next Moves:

  1. Replace manual output with Angular's built-in pipes in your project.
  2. Create a custom pipe for a unique requirement in your project.
  3. Pass this guide to your team!

Ask us any further questions by adding a comment below.

External Links:

Angular Camera Integration

Angular Camera Integration

Capture Photos and Stream Video in Your Web App

Suppose you are making a fitness app where users scan barcodes to log their meals before the end of the day. Or perhaps you are building a social platform where users take live profile pictures in real-time—no uploads required! Camera integration in web apps enables these functionalities.

How can this be done? Camera integration is a recent yet case-sensitive development. With Angular, you can:

  1. Use the user's camera in your app.
  2. Display live video and capture snapshots smoothly.
  3. Handle errors and permission denials appropriately

If your app uses a camera, it will stand out. Let’s explore how to integrate it effectively.

Step 1: Equipment Needed for Angular Projects

Before you begin:

  • Install the Angular CLI.
  • Have a basic understanding of components/services.
  • Create a new component for camera handling:
    ng generate component camera
    

Step 2: Using the Camera

Request Permissions and Start Streaming

Add the following to Camera.Component.ts:

import { Component, OnDestroy, ViewChild, ElementRef } from '@angular/core';

@Component({
  selector: 'app-camera',
  templateUrl: './camera.component.html',
  styleUrls: ['./camera.component.css']
})
export class CameraComponent implements OnDestroy {
  @ViewChild('videoElement') videoElement!: ElementRef;
  @ViewChild('canvasElement') canvasElement!: ElementRef;
  private mediaStream: MediaStream | null = null;
  photoUrl: string | null = null;

  async startCamera() {
    try {
      this.mediaStream = await navigator.mediaDevices.getUserMedia({
        video: { facingMode: 'user' },
        audio: false
      });
      if (this.videoElement.nativeElement) {
        this.videoElement.nativeElement.srcObject = this.mediaStream;
        this.videoElement.nativeElement.play();
      }
    } catch (err) {
      console.log('Camera access denied:', err);
      alert('Please turn on camera access!');
    }
  }

  ngOnDestroy() {
    this.mediaStream?.getTracks().forEach(track => track.stop());
  }
}

Step 3: Viewing the Live Video Stream

Add the following code to Camera.Component.html:

<button (click)="startCamera()">Enable Camera Capture</button>
<video #videoElement autoplay></video>

Step 4: Taking a Photo from the Stream

Add the following:

TakePhoto() {
  const video = this.videoElement.nativeElement;
  const canvas = this.canvasElement.nativeElement;
  canvas.width = video.videoWidth;
  canvas.height = video.videoHeight;
  const ctx = canvas.getContext('2d');
  ctx?.drawImage(video, 0, 0, canvas.width, canvas.height);
  this.photoUrl = canvas.toDataURL('image/png');
}

Universal Hardware Solutions to Common Problems

1. Access Rights Errors

  • Solution:
    • Use HTTPS in production (HTTP can be blocked by browsers).
    • Direct users to their browser settings to enable camera access.

2. Cross-Browser Compatibility

  • Solution: Ensure support for Chrome, Firefox, and Safari.

  • Perform feature detection:

    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
      // Supported
    } else {
      alert('This browser does not support camera access.');
    }
    

3. Mobile Resizing

  • Solution: Add responsive CSS:

    video {
      width: 100%;
      max-width: 500px;
      border-radius: 8px;
    }
    

Expert Advice for Production-Ready Camera Apps

1. High Definition Video Capture Quality

getUserMedia({ video: { width: { ideal: 1920 }, height: { ideal: 1080 } } });

2. Add a Flash/Torch for Rear Camera

const track = this.mediaStream?.getVideoTracks()[0];  
track?.applyConstraints({ advanced: [{ torch: true }] });  

Key Takeaways

1. Browser APIs

  • Chrome, Firefox, and Safari support navigator.mediaDevices.
  • getUserMedia() requests camera access and returns a MediaStream.

2. Security Rules

  • Apps must run on HTTPS (or localhost).
  • Users must give explicit permission for camera access.

3. Angular Integration

  • Angular components/services wrap browser APIs for seamless reactivity.

  • Always clean up resources in ngOnDestroy():

    ngOnDestroy() {
      this.mediaStream?.getTracks().forEach(track => track.stop());
    }

By combining Angular’s architecture with the MediaStream API, you can create camera-integrated web apps that are both secure and user-friendly. Start small—implement live video streaming, then move on to photo capture.

Angular standalone components

Angular Standalone Components Make Your Code Cleaner, More Productive

Imagine setting up an Angular component is like assembling an IKEA piece of furniture—without the instruction manual. Even a simple button can turn into a configuration nightmare with declarations, imports, and exports in NgModules.

Enter standalone components—Angular’s way of eliminating boilerplate code and making development cleaner and more maintainable.

In this guide, you will learn:

✅ What standalone components are (and why they are a game-changer).
✅ How to craft your own Angular standalone components.
✅ Replacing NgModules with real-world examples.
✅ Tips to avoid common mistakes.

Let's clean up your Angular workflow! 🚀


Why Standalone Components Were Introduced

With Angular 14, standalone components eliminate the need for NgModules when building self-contained UI widgets.

What Are Standalone Components?

Standalone components are self-contained Angular components that declare their own dependencies (directives, pipes, services) directly in the @Component decorator. They are modular chunks of UI that you can easily drop into any project.

Standalone vs. Traditional Components: A Quick Comparison

Feature Traditional Component Standalone Component
NgModule Required? Yes No
Dependency Management Handled in NgModule Declared in component decorator
Ease of Reuse Requires module export Import directly anywhere
Ideal Usage Large apps with shared modules Micro frontends, lazy loading


How to Write a Standalone Component

Step 1: Generate the Component using --standalone

Run the following Angular CLI command:

ng generate component button --standalone

Step 2: Declare Dependencies

Modify button.component.ts to import and declare dependencies:

import { Component, Input } from '@angular/core';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-button',
  standalone: true,
  imports: [CommonModule], // Import necessary modules here
  template: `<button>{{variant}}</button>`
})
export class ButtonComponent {
  @Input() variant: 'primary' | 'secondary' = 'primary';
}

Step 3: Use It Anywhere!

No modifications to app.module.ts are needed—just import it into another component:

import { ButtonComponent } from './button.component';

@Component({
  standalone: true,
  imports: [ButtonComponent],
  template: `<app-button variant="primary">Click Me</app-button>`
})
export class ExampleComponent {}

5 Practical Use Cases for Standalone Components

  1. Shared UI Library: Create reusable buttons, modals, and cards without requiring a shared module.
  2. Lazy-Loading Routes: Load standalone components on demand to improve startup time.
  3. External Integrations: Integrate components smoothly across different teams.
  4. Third-Party Embeds: Easily embed charts, maps, or widgets.
  5. Legacy App Migration: Gradually modernize legacy Angular codebases.

Common Pitfalls (and How to Avoid Them)

🚫 Circular Dependencies: When Component A uses Component B, and vice versa.
Fix: Use Angular’s forwardRef() or redesign your structure.

🚫 Overusing imports in Decorators: Unnecessarily cluttering the decorator with unused modules.
Fix: Only import what the component directly needs.

🚫 Forgetting to Register Services: Some services may be missing at runtime.
Fix: Add providers: [YourService] to the decorator when needed.

Pro Tips for Standalone Success

💡 Start Small: Convert simple components (e.g., buttons, inputs) first.
💡 Leverage Angular CLI: Use the --standalone flag for components, directives, and pipes.
💡 Use provideRouter for Standalone Applications:

bootstrapApplication(AppComponent, {
  providers: [provideRouter([{ path: 'home', component: HomeComponent }])]
});

💡 Combine with Signals: Use Angular’s new reactive state management with standalone components.

Standalone components aren't just a feature—they're the future of Angular development. By ditching NgModules, developers gain cleaner, faster, and more maintainable code.


End-to-End Testing with Cypress and Angular

End-to-End Testing using Cypress and Angular - A Beginner’s Guide to Flawless Apps

The Importance of End-to-End Testing for Angular Apps

E2E testing is the ultimate functionality test, simulating user interactions from start to finish. This is crucial for Angular apps because:

  • Complex behaviors: Angular handles dynamic UIs (forms, routers, APIs) that need careful inspection.
  • High user expectations: 88% of users will abandon an app after two failures
  • Faster releases: Teams using E2E testing see fewer production errors 

Cypress excels here with rapid reloading, real-time automatic waits, and a clear syntax, making it a top choice for Angular developers.

Setting Up Cypress in Your Angular Project

Prerequisites

  1. Angular CLI installed.
  2. A basic understanding of Angular components and services.

Step 1: Install Cypress

Run the following in your project directory:

npm install cypress --save-dev  

Step 2: Open Cypress

Initialize Cypress and generate boilerplate files:

npx cypress open  

Cypress creates a cypress folder with example tests.

Step 3: Configure Cypress for Angular

Update cypress.config.ts:

import { defineConfig } from 'cypress';  
export default defineConfig({  
  e2e: {  
    baseUrl: 'http://localhost:4200', // Your Angular dev server URL  
    setupNodeEvents(on, config) {},  
    specPattern: 'cypress/e2e/**/*.spec.ts',  
  },  
});  

Writing Your First E2E Test

Let's create an E2E test for a todo list app where users can add or delete tasks.

1. Create the Test File

Write cypress/e2e/todo.spec.ts:

describe('Todo List', () => {  
  beforeEach(() => {  
    cy.visit('/'); // Navigate to the app homepage  
  });  
  it('can enter a new item', () => {  
    cy.get('[data-cy=todo-input]').type('Learn Cypress{enter}');  
    cy.get('[data-cy=todo-list] li').should('have.length', 1);  
  });  
  it('will remove an item', () => {  
    cy.get('[data-cy=todo-input]').type('Delete this{enter}');  
    cy.get('[data-cy=delete-btn]').click();  
    cy.get('[data-cy=todo-list] li').should('not.exist');  
  });  
});  

2. Run the Test

npx cypress open  

Click the test file and watch Cypress execute in real-time!

5 Pro Tips for Effective Cypress Tests

  1. Use custom data attributes (data-cy=xyz) to avoid brittle selectors.
  2. Mock API Responses using cy.intercept() to test without backend dependency.
  3. Use Hooks like beforeEach to reset state between tests.
  4. Accessibility Testing: Use cy.injectAxe() and cy.checkA11y() from the cypress-axe plugin.
  5. CI/CD Integration: Run tests in headless mode:
npx cypress run --headless --browser chrome  

Most Common Cypress Pitfalls (And How to Avoid Them)

  • Dynamic elements: Use .contains() or .find() to handle changing elements.
  • Flaky tests: Ensure stable selectors, avoid timing issues, and use cy.wait() wisely.
  • Modifying app code to fit tests: Never change real code just to pass tests!

Real-World Example: Testing an Angular Auth Flow

Imagine a login process with error handling.

describe('Login Flow', () => {  
  it('should log in successfully', () => {  
    cy.visit('/login');  
    cy.get('[data-cy=email]').type('user@test.com');  
    cy.get('[data-cy=password]').type('password123{enter}');  
    cy.url().should('include', '/dashboard');  
  });  
  it('should show error message if login fails', () => {  
    cy.intercept('POST', '/api/login', { statusCode: 401 });  
    cy.visit('/login');  
    cy.get('[data-cy=username]').type('wrong@test.com');  
    cy.get('[data-cy=password]').type('wrong{enter}');  
    cy.get('[data-cy=message]').should('contain', 'Invalid username or password.');  
  });  
});  

Using Cypress for E2E testing ensures that your Angular app functions smoothly for real users. Early bug detection saves time, reduces stress, and builds user trust.

External Links:


unit Testing in Angular services

Master Unit Testing in Angular: A Beginner's Guide to Jasmine and Karma

Introduction: Why Unit Testing is Something You Can't Ignore

  • What are Jasmine and Karma?
  • How to use them properly to test both components and services.
  • Real examples (Yes, we will!) such as reading from an external data source or engaging in user interactions.
  • Helpful hints for beginners and advanced users alike.

By the end, you gain the ability to write tests that handle adversity while bringing pleasure and satisfaction every time they pass!

Jasmine & Karma: Descriptions & Connections

Jasmine: The Testing Framework

Jasmine is a behavior-driven development (BDD) framework for human-readable test writing. It uses natural language, making it easy to write and read tests.

Characteristic Features:

  • describe(): Groups related tests (test suites).
  • it(): Defines individual test cases.
  • expect(): Asserts that something will happen.

Karma: The Test Runner

Karma runs your tests in real browsers (Chrome, Firefox) or various "headless" environments. Moreover, Karma re-runs tests after changes in source code.

Why Join Them Together?

  • Jasmine writes the tests.
  • Karma executes those tests across different browsers.
  • Both integrate seamlessly with Angular's CLI.

How to Set Jasmine & Karma Up within Angular

1. Angular CLI: It's All Built In

When starting a new Angular project, Jasmine and Karma come pre-configured. Simply check the files karma.conf.js and src/test.ts to see how things are set up.

2. Running Your First Test

ng test

This command launches Karma, executes all tests, and displays a live-running browser with outputs.

Writing Your First Test

Example: Testing a Simple Service

We'll test a CalculatorService with an add() method.

Step 1: Create the Service

// calculator.service.ts
@Injectable({
  providedIn: 'root'
})
export class CalculatorService {
  add(a: number, b: number): number {
    return a + b;
  }
}

Step 2: Write the Test

// calculator.service.spec.ts
describe('CalculatorService', () => {
  let service: CalculatorService;

  beforeEach(() => {
    TestBed.configureTestingModule({});
    service = TestBed.inject(CalculatorService);
  });

  it('should add two numbers', () => {
    expect(service.add(2, 3)).toBe(5);
  });

  it('should handle negative numbers', () => {
    expect(service.add(-1, 5)).toBe(4);
  });
});

Step 3: Run the Test

ng test

Karma reports back with either green checks (for passing tests) or red Xs (for failures).


Real Test Scenarios

1. Components with User Interaction

This test checks if a LoginComponent emits its login event when the button is clicked.

// login.component.spec.ts
it('should emit login event on button click', () => {
  spyOn(component.login, 'emit');
  const button = fixture.nativeElement.querySelector('button');
  button.click();
  expect(component.login.emit).toHaveBeenCalled();
});

2. Mocking HTTP Requests

Testing a DataService that fetches data from an API using HttpClientTestingModule.

// data.service.spec.ts
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';

describe('DataService', () => {
  let httpMock: HttpTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule]
    });
    httpMock = TestBed.inject(HttpTestingController);
  });

  it('should fetch users', () => {
    const mockUsers = [{ id: 1, name: 'John' }];
    service.getUsers().subscribe(users => {
      expect(users).toEqual(mockUsers);
    });
    const req = httpMock.expectOne('/api/users');
    req.flush(mockUsers);
    httpMock.verify();
  });
});

Best Practices for Successful Testing

  1. Make each it() focused: Tests should be concise and targeted.
  2. Use Descriptive Names: it('should print error when email is invalid') > it('testing form').
  3. Mock Dependencies: Use spies or stubs to isolate tests.
  4. Prioritize Critical Paths: Focus on areas with the highest user impact.
  5. Always Run Tests: Integrate tests into CI/CD pipelines (e.g., GitHub Actions).


Angular Unit Testing Mastery: Jasmine, Karma, and Component Testing Explained

Angular Unit Testing Mastery: Jasmine, Karma, and Component Testing Explained

Angular Unit Testing Mastery: Jasmine, Karma, and Component Testing Explained

Why Component Testing Is Crucial

You’ve written a stunning Angular component. It looks perfect, but do you know if it produces the expected results?
Manual testing is too time-consuming and prone to error.

Component testing with Jasmine and Karma automates the process, allowing you to validate your UI logic and user interactions in milliseconds.

This guide includes the following topics:

  • How to test Angular components (including inputs, outputs, and DOM interactions)
  • Advanced techniques on services, HTTP calls, and user events
  • Tools such as TestBed, ComponentFixture, and Spies
  • Real-world examples to bulletproof your app.

Step by Step for Component Testing

1. Create a Simple Counter Component

Let's write a CounterComponent with increment/decrement buttons.

// counter.component.ts
@Component({
  selector: 'app-counter',
  template: ` 
    <button (click)="decrement()">-</button>
    {{ count }}
    <button (click)="increment()">+</button>
  `
})
export class CounterComponent {
  count = 0;
  increment() { this.count++; }
  decrement() { this.count--; }
}

2. Write Test for Component's Initial State, Button Click, and UI Updates

// counter.component.spec.ts
describe('CounterComponent', () => {
  let component: CounterComponent;
  let fixture: ComponentFixture<CounterComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [CounterComponent]
    }).compileComponents();

    fixture = TestBed.createComponent(CounterComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should start with count 0', () => {
    expect(component.count).toBe(0);
  });

  it('should increment count when + is clicked', () => {
    const button = fixture.nativeElement.querySelector('button:last-child');
    button.click();
    fixture.detectChanges();
    expect(component.count).toBe(1);
    expect(fixture.nativeElement.querySelector('span').textContent).toBe('1');
  });

  it('should decrement count when - is clicked', () => {
    component.count = 5;
    fixture.detectChanges();
    const button = fixture.nativeElement.querySelector('button:first-child');
    button.click();
    expect(component.count).toBe(4);
  });
});

Take Home Points:

  • TestBed: Sets up the test environment (like NgModule).
  • ComponentFixture: Wraps component instance and template.
  • detectChanges(): Makes Angular detect changes and update the view.

Testing Component Inputs and Outputs

Example: Notification Banner Component

Test a component that takes an @Input() message and emits an @Output()on dismissal.

// notification.component.ts
@Component({
  selector: 'app-notification',
  template: `
    <div>
      {{ message }}
      <button (click)="dismiss()">Close</button>
    </div>
  `
})
export class NotificationComponent {
  @Input() message: string;
  @Output() closed = new EventEmitter<void>();

  dismiss() {
    this.closed.emit();
  }
}

Test Cases

// notification.component.spec.ts
it('should display the input message', () => {
  component.message = 'Hello!';
  fixture.detectChanges();
  const div = fixture.nativeElement.querySelector('div');
  expect(div.textContent).toContain('Hello!');
});

it('should emit event when dismissed', () => {
  component.message = 'Test';
  fixture.detectChanges();
  spyOn(component.closed, 'emit');
  const button = fixture.nativeElement.querySelector('button');
  button.click();
  expect(component.closed.emit).toHaveBeenCalled();
});

Testing Component with Dependencies

Test a UserProfileComponent that relies on a UserService

1. Mock the Dependency

// user.service.mock.ts
class MockUserService {
  getUser() {
    return of({ name: 'Alice', email: 'alice@test.com' });
  }
}

// user-profile.component.spec.ts
beforeEach(() => {
  TestBed.configureTestingModule({
    declarations: [UserProfileComponent],
    providers: [
      { provide: UserService, useClass: MockUserService }
    ]
  });

  fixture = TestBed.createComponent(UserProfileComponent);
});

2. Test Data Rendering

it('should display user data', () => {
  fixture.detectChanges(); // Triggers ngOnInit
  const nameEl = fixture.nativeElement.querySelector('.name');
  expect(nameEl.textContent).toContain('Alice');
});

Best Practices for Component Testing

  1. Test Templates and Logic: Validate both UI and component class logic.
  2. Use async/fakeAsync: Handle timers and asynchronous operations.
  3. Leverage Angular Utilities: DebugElement, By.css(), and triggerEventHandler().
  4. Isolate Tests: Mock services to avoid HTTP calls or state leaks.

Advanced Techniques

Simulating User Input with fakeAsync

Test a form input with debounce:

import { fakeAsync, tick } from '@angular/core/testing';

it('should update search term after debounce', fakeAsync(() => {
  const input = fixture.nativeElement.querySelector('input');
  input.value = 'test';
  input.dispatchEvent(new Event('input'));
  tick(300); // Fast-forward 300ms
  expect(component.searchTerm).toBe('test');
}));

Code Coverage Reports

Generate coverage stats:

ng test --code-coverage

Open coverage/index.html to see which paths lack test coverage.

Conclusion: Key Point Rating

Testing components by simulating clicks, inputs, and outputs
TestBed for module setup and dependency simulation
Combine unit tests with integration tests for full code coverage


Select Menu