Bird
Raised Fist0
Angularframework~10 mins

tap operator for side effects in Angular - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - tap operator for side effects
Observable emits value
↓
tap operator intercepts value
↓
Execute side effect (e.g., log)
↓
Pass original value unchanged
↓
Subscriber receives value
The tap operator lets you run code (side effects) when an Observable emits, without changing the emitted value.
Execution Sample
Angular
import { of } from 'rxjs';
import { tap } from 'rxjs/operators';

of(1, 2, 3).pipe(
  tap(x => console.log('Side effect:', x))
).subscribe(x => console.log('Received:', x));
This code logs each emitted value as a side effect, then the subscriber receives the same values.
Execution Table
StepObservable emitstap side effectValue passed onSubscriber output
11console.log('Side effect: 1')1console.log('Received: 1')
22console.log('Side effect: 2')2console.log('Received: 2')
33console.log('Side effect: 3')3console.log('Received: 3')
4CompleteNo side effectNo valueSubscription ends
💡 Observable completes after emitting all values; tap runs side effects without altering values.
Variable Tracker
VariableStartAfter 1After 2After 3Final
emittedValuenone123complete
sideEffectLogempty'Side effect: 1''Side effect: 2''Side effect: 3'done
subscriberReceivednone123complete
Key Moments - 2 Insights
Why doesn't tap change the values received by the subscriber?
Because tap only runs side effects and passes the original value unchanged, as shown in execution_table rows 1-3 where the value passed on equals the emitted value.
What happens if the tap callback throws an error?
An error in tap will propagate and stop the Observable chain, but normally tap is used for safe side effects like logging, as shown in the example where no errors occur.
Visual Quiz - 3 Questions
Test your understanding
Look at the execution table, what value does the subscriber receive at step 2?
A1
B2
CSide effect: 2
DNo value
💡 Hint
Check the 'Subscriber output' column at step 2 in the execution_table.
At which step does the Observable complete?
AStep 4
BStep 3
CStep 2
DNever completes
💡 Hint
Look at the 'Observable emits' column and the exit_note in the execution_table.
If the tap operator was removed, what would change in the subscriber output?
ASubscriber would receive different values
BSubscriber would receive no values
CSubscriber would receive the same values without side effect logs
DObservable would not complete
💡 Hint
Refer to variable_tracker and understand tap only adds side effects without changing values.
Concept Snapshot
tap operator runs side effects on Observable emissions
It does NOT change the emitted values
Useful for logging, debugging, or other side effects
Values pass through unchanged to subscribers
Errors in tap propagate and stop the stream
Commonly used inside pipe() in Angular RxJS
Full Transcript
The tap operator in Angular RxJS lets you perform side effects like logging when an Observable emits values. It intercepts each emitted value, runs your side effect code, and then passes the original value unchanged to the subscriber. This means the subscriber receives exactly what the Observable emits. The example code shows an Observable emitting 1, 2, and 3. The tap operator logs each value as a side effect, then the subscriber logs the received value. The execution table traces each step: the Observable emits a value, tap logs it, then the subscriber receives it. When the Observable completes, the subscription ends. This helps beginners see that tap is for side effects only and does not alter the data flow.

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