Bird
Raised Fist0
Angularframework~5 mins

tap operator for side effects in Angular

Choose your learning style10 modes available
Introduction

The tap operator lets you run extra code when data flows through an observable without changing the data itself.

Logging values passing through an observable to see what data is flowing.
Triggering a loading spinner when a request starts and stops.
Performing side actions like updating a variable or calling a function without affecting the main data.
Debugging observable streams by inspecting values without modifying them.
Syntax
Angular
observable$.pipe(
  tap(value => {
    // side effect code here
  })
)

The tap operator does not change the data passing through.

It is used inside the pipe method of an observable.

Examples
Logs each value passing through without changing it.
Angular
import { tap } from 'rxjs/operators';

myObservable.pipe(
  tap(value => console.log('Value:', value))
).subscribe();
Runs different side effects for next, error, and complete events.
Angular
myObservable.pipe(
  tap({
    next: value => console.log('Next:', value),
    error: err => console.error('Error:', err),
    complete: () => console.log('Done')
  })
).subscribe();
Sample Program

This Angular component creates an observable of numbers 1, 2, and 3. The tap operator logs each value as a side effect. The subscribe then logs the received values.

Angular
import { Component } from '@angular/core';
import { of } from 'rxjs';
import { tap } from 'rxjs/operators';

@Component({
  selector: 'app-tap-example',
  template: `<p>Check the console for tap operator output.</p>`
})
export class TapExampleComponent {
  constructor() {
    of(1, 2, 3).pipe(
      tap(value => console.log('Side effect:', value))
    ).subscribe(value => console.log('Received:', value));
  }
}
OutputSuccess
Important Notes

Use tap only for side effects, not to change data.

Side effects run every time the observable emits a value.

Summary

tap lets you peek at data without changing it.

It is great for logging, debugging, or triggering side actions.

Always use it inside pipe with observables.

Practice

(1/5)
1. What is the main purpose of the tap operator in Angular's RxJS?
easy
A. To transform the data emitted by an observable
B. To perform side effects without modifying the data stream
C. To filter out unwanted values from the observable
D. To combine multiple observables into one

Solution

  1. Step 1: Understand the role of tap

    The tap operator allows you to execute side effects like logging or debugging without changing the data passing through the observable.
  2. Step 2: Compare with other operators

    Operators like map transform data, filter removes values, and combineLatest merges streams. tap only peeks at data.
  3. Final Answer:

    To perform side effects without modifying the data stream -> Option B
  4. Quick Check:

    tap = side effects only [OK]
Hint: Remember: tap is for side effects, not data changes [OK]
Common Mistakes:
  • Thinking tap changes the data
  • Confusing tap with map or filter
  • Using tap outside pipe
2. Which of the following is the correct syntax to use tap inside an observable pipe in Angular?
easy
A. observable.pipe(map(value => tap(console.log(value))))
B. observable.tap(value => console.log(value))
C. tap(observable, value => console.log(value))
D. observable.pipe(tap(value => console.log(value)))

Solution

  1. Step 1: Recall the RxJS pipe syntax

    Operators like tap are used inside the pipe() method of an observable.
  2. Step 2: Check each option

    observable.pipe(tap(value => console.log(value))) correctly uses pipe(tap(...)). observable.tap(value => console.log(value)) tries to call tap directly on observable, which is invalid. observable.pipe(map(value => tap(console.log(value)))) misuses map and tap. tap(observable, value => console.log(value)) is not valid RxJS syntax.
  3. Final Answer:

    observable.pipe(tap(value => console.log(value))) -> Option D
  4. Quick Check:

    Use pipe(tap(...)) syntax [OK]
Hint: Always put tap inside pipe() for observables [OK]
Common Mistakes:
  • Calling tap directly on observable
  • Mixing tap with map incorrectly
  • Using tap outside pipe
3. Given the following code, what will be logged to the console?
import { of } from 'rxjs';
import { tap, map } from 'rxjs/operators';

of(1, 2, 3).pipe(
  tap(x => console.log('Before map:', x)),
  map(x => x * 10),
  tap(x => console.log('After map:', x))
).subscribe();
medium
A. Before map: 1 After map: 10 Before map: 2 After map: 20 Before map: 3 After map: 30
B. After map: 10 After map: 20 After map: 30
C. Before map: 10 Before map: 20 Before map: 30
D. Before map: 1 Before map: 2 Before map: 3

Solution

  1. Step 1: Understand the order of operators

    The observable emits 1, 2, 3. The first tap logs 'Before map:' with original values. Then map multiplies each by 10. The second tap logs 'After map:' with transformed values.
  2. Step 2: Trace the console logs

    For each value: log 'Before map: x', then after map, log 'After map: x*10'. So logs appear in pairs for each value.
  3. Final Answer:

    Before map: 1 After map: 10 Before map: 2 After map: 20 Before map: 3 After map: 30 -> Option A
  4. Quick Check:

    tap logs before and after map [OK]
Hint: tap logs values without changing them [OK]
Common Mistakes:
  • Assuming tap changes values
  • Ignoring order of operators
  • Expecting only one set of logs
4. Identify the error in this code snippet using tap and fix it:
import { of } from 'rxjs';
import { tap } from 'rxjs/operators';

const source = of(5, 10, 15);
source.tap(value => console.log(value)).subscribe();
medium
A. Replace source.tap(...) with source.pipe(tap(...))
B. Import tap from 'rxjs' instead of 'rxjs/operators'
C. Add subscribe() before tap()
D. Change of to from

Solution

  1. Step 1: Identify incorrect usage of tap

    The code calls tap directly on the observable, which is invalid. Operators must be used inside pipe().
  2. Step 2: Correct the syntax

    Change source.tap(...) to source.pipe(tap(...)) to properly apply the operator.
  3. Final Answer:

    Replace source.tap(...) with source.pipe(tap(...)) -> Option A
  4. Quick Check:

    Use pipe(tap(...)) not tap() directly [OK]
Hint: Operators go inside pipe(), never called directly [OK]
Common Mistakes:
  • Calling tap directly on observable
  • Wrong import path for tap
  • Missing pipe() usage
5. You want to log each value emitted by an HTTP request observable without changing the data. Which is the best way to do this using tap? Consider this code:
this.http.get('/api/data').pipe(
  // What goes here?
).subscribe(data => this.process(data));
hard
A. subscribe(data => console.log('Received:', data))
B. map(data => { console.log('Received:', data); return data; })
C. tap(data => console.log('Received:', data))
D. filter(data => { console.log('Received:', data); return true; })

Solution

  1. Step 1: Understand the goal

    You want to log data without changing it before passing it to subscribe.
  2. Step 2: Choose the operator that logs without modifying

    tap is designed for side effects like logging without altering data. map transforms data, filter filters it, and logging inside subscribe happens after pipe.
  3. Final Answer:

    tap(data => console.log('Received:', data)) -> Option C
  4. Quick Check:

    tap logs side effects without data change [OK]
Hint: Use tap inside pipe for logging without data change [OK]
Common Mistakes:
  • Using map for logging (changes data)
  • Logging inside subscribe instead of pipe
  • Using filter which may drop data
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard