angularrxjsangular2-changedetectionangular16angular-signals

How does Angular Signals affect RXJS Observables and what happens to change detection in this case


I am trying to switch to Signals in my Angular App and i currently have issues finding good practices and i also experienced (at least for me) unexpected behaviour.

This is a snippet of my code consisting of a userService and two Components

export class UserService {
  private user$: Observable<User>;
  private refreshUser$ = new BehaviorSubject<void>(undefined);

  constructor(private readonly http: HttpClient) {}

  getUser(): Observable<User> {
    if (!this.user$) {
      this.user$ = this.refreshUser$.pipe(
        switchMap(() => this.http.get<User>('...')),
        shareReplay(1)
      );
    }
    return this.user$;
  }

  reloadUser(): void {
    this.refreshUser$.next();
  }
}

Then i have Component A which subscribes for the User:

export class aComponent {

 currentUser: User;

  constructor(private readonly userService: UserService) {
    this.userService.getUser().subscribe((user) => {
      this.currentUser = user;
    });
 }
}

And i have Component aChild which is a child of Component A and also subscribes for the User and also can manipulate the User. It is able to change/remove the address of the User. Before using Signals i would update the address of my currentUser Object of the Component instantly so the user could instantly see the Result and i would call "reloadUser()" from the Service so that the global User is up to date and the "A Component" has the right value (this might not be the best solution because it leads to another http request but thats not the topic here).

export class aChildComponent {

 currentUser: User;

  constructor(private readonly userService: UserService) {
    this.userService.getUser().subscribe((user) => {
      this.currentUser = user;
    });
 }


 changeAddress(newAddress: any) {
  this.userService.updateAddress(newAddress).subscribe((updatedAddress) => {
   this.currentUser.address = updatedAddress;
   this.userService.reloadUser();
  }
 }
}

After i have implemented the aChild Component with signals i have found, that i do not have to call "reloadUser()" anymore and that the value of the "currentUser" Object of Component A already has the updated address.

export class aChildComponent {

  currentUser: WritableSignal<User | undefined> = signal(undefined);

  constructor(private readonly userService: UserService) {
    this.userService.getUser().subscribe((user) => {
      this.currentUser.set(user);
    });
 }


 changeAddress(newAddress: any) {
  this.userService.updateAddress(newAddress).subscribe((updatedAddress) => {
   this.currentUser.mutate((user) => (user.address = updatedAddress));
  }
 }
}

I do not know if this is expected behaviour but an anti pattern or if it is totally unexcpected or if this behaviour is totally fine. However i do not fully understand why this is happening and how it works becouse in the "A Component" the subscription on getUser() is not fired after mutating the signal in the child component. The value changes what seems like per reference. I am also concerned how change detection would work in this case when there is no more zone.js and the application works solely with Signals. Since in the "A Component" the currentUser is not a Signal but its address changes anyways (through some magic) how is this change detected in my html code?


Solution

  • I actually did a mistake and a misinterpretation. Signals were not the cause of this behaviour and it was actually the same as without signals.

    When a subscriber changes the value of a replaysubject it seems that the value is changed on the subject as well without emitting.

    I guess if i do not want this behaviour i should clone the object.