Question

Angular Signals: How to trigger an effect based on a signal without using its value?

I'm working with Angular and trying to use signals within an effect(). My goal is to trigger an action when a specific signal changes, but the action itself doesn't need the signal's value.

For example, I have an isAuthorized signal, and I want to call fetchCars() whenever the authorization status changes, but fetchCars() doesn't need to use the isAuthorized value.

constructor() {
  effect(() => {
    const isAuthorized = this.authService.userAuthorized();
    
    // This function doesn't use the value of isAuthorized anyway.
    this.fetchCars();
  });
}

It works but it's a bit odd to assign isAuthorized to a constant without actually using it. Is there a more correct or elegant way to achieve this?

Angular Version: 18.0.1

 3  60  3
1 Jan 1970

Solution

 2

You can use toObservable() to convert the signal to an Observable:

constructor() {
  toObservable(this.authService.userAuthorized)
    .pipe(takeUntilDestroyed())
    .subscribe(() => this.fetchCars())
}

toObservable is part of the RxJS Interop package which is currently in developer preview and might undergo (breaking) changes before becoming stable.

2024-07-19
JSON Derulo

Solution

 1

I couldn't find a better way other than to either use the toObservable() as mentioned by @JSONDerulo or to trigger the signal getter inside the effect().

However you could define a named effect variable in the service that triggers the signal getter so that you don't have to trigger it in every single component that uses the effect.

Try the following

Service

import { Injectable, signal, effect } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class AuthService {
  public readonly userAuthorized = signal(false);
  public readonly userAuthorizedEffect = (callback: () => void) =>
    effect(() => {
      this.userAuthorized();
      callback();
    });

  public toggleAuthStatus(): void {
    this.userAuthorized.set(!this.userAuthorized());
  }
}

Component

export class App {
  constructor(private readonly authService: AuthService) {
    this.authService.userAuthorizedEffect(() => {
      this.fetchCars();
    });
  }

  fetchCars() {
    console.log('fetching cars here...');
  }
}

Working example: Stackblitz

2024-07-19
Michael D