DEV Community

Cover image for Angular Tips #2
Khoa Nguyen
Khoa Nguyen

Posted on

Angular Tips #2

Angular Tips
Some Tips for Working with Angular — From a Frontend Developer — Part 2.


Let’s continue the series on tips for working with Angular — the most challenging framework.

As always, let me know in the comments if you find this interesting to read or if there’s anything I can improve. I’d love to hear your feedback!

Note: These tips require prior knowledge and experience with Angular. I will not dive deep into RxJS or other fundamental concepts.


Get and Use the Previous Value of a BehaviorSubject

There are times when we need to retrieve the previous value of a BehaviorSubject for calculations. For example, increasing the current value by 1. There are two ways to do this:

1/ Using the the getValue() function of behavior subject.
For this approach, you can use this like:

protected increaseVal1() { 
  this.var1$.next(this.var1$.getValue() + 1);
}
Enter fullscreen mode Exit fullscreen mode

This approach is straight-forward and easy to implement. However, in some projects, accessing the value property of a BehaviorSubject is forbidden, meaning you cannot use the getValue() function freely. There are specific reasons for this, but I won’t dive deep into them in this article. If you're interested, I recommend researching it further on Google.

So, if we can’t use getValue(), how can we achieve this?
2/ Using observables
With a little RxJS magic, you can achieve the same result using observables:

 protected increaseVal1() {
    this.var1$
      .pipe(
        first(),
        tap((v) => this.var1$.next(v + 1)),
      )
      .subscribe();
  }
Enter fullscreen mode Exit fullscreen mode

The first operator ensures that we only take the initial value from the stream and then unsubscribe immediately, preventing an infinite loop or memory leaks. Therefore, using first() or take(1) is essential here.

This approach does not violate any best practices or project rules, so I often use it in my projects. Hope this helps you as well!

Use the debounceTime operator when a value changes multiple times, but you only need the source to emit once.

Sometimes, in our projects, we encounter situations where we need the source to emit only once, even if the value is updated multiple times. For example, consider the following component:

import { Component, OnInit } from "@angular/core";
import { BehaviorSubject, first, skip, tap } from "rxjs";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.scss"],
})
export class AppComponent implements OnInit {
  protected readonly var1$ = new BehaviorSubject(100);

  ngOnInit(): void {
    this.var1$
      .asObservable()
      .pipe(
        skip(1),
        tap((val1) => console.log({ val1 })),
      )
      .subscribe();
  }

  protected increaseVal1() {
    this.var1$
      .pipe(
        first(),
        tap((v) => this.var1$.next(v + 1)),
      )
      .subscribe();
  }

  protected changeValue1() {
    this.increaseVal1();
    this.increaseVal1();
  }
}
Enter fullscreen mode Exit fullscreen mode
<div>
  <p>Value 1: {{ var1$ | async }}</p>
  <button (click)="changeValue1()">Increase Value 1</button>
</div>
Enter fullscreen mode Exit fullscreen mode

And the UI for this code looks like this:

Image description
When I click the “Increase Value 1” button, the logs are:
Image description

As you can see, the tap operator is called twice because the increaseVal1 function is executed twice synchronously within the changeValue1 function.

In some cases, you might want the tap operator to execute only once, using the latest value, even if the function that changes the value is called multiple times.

How can we achieve that? Simple — just add the debounceTime(0) operator beforehand.

  ngOnInit(): void {
    this.var1$
      .asObservable()
      .pipe(
        skip(1),
        debounceTime(0),
        tap((val1) => console.log({ val1 })),
      )
      .subscribe();
  }
Enter fullscreen mode Exit fullscreen mode

Now, when you click the button, the logs will include only the latest value, meaning the tap operator is executed only once.

Image description
When you add debounceTime(0), RxJS delays the emissions until the current event loop cycle ends, so:

  • Both pipe(...).subscribe() calls happen in the same JavaScript tick.
  • Since debounceTime(0) defers execution, both calls collapse into one before emitting a value.
  • As a result, console.log in tap operator only run once.
  • In real-world projects, this scenario might be rare, but if you ever encounter it, you can use this approach to handle it effectively.

And that’s all for blog #2 about the Angular tips! I hope you found these tips helpful. See you in my next blog!

Top comments (0)