4

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?

  • Where is the `currentUser` signal being read? In the aComponent Or in the aChildComponent? Could you put together a little stackblitz that demonstrates the issue (not the entire app, just the issue with a faked http call)? That would make it easier to see more specifically what's going on. – DeborahK May 31 '23 at 06:55
  • IMO, signals are only good for synchronous changes. RxJS will keep being very useful for async code. See this article which I did disagree with a lot: https://levelup.gitconnected.com/signals-in-angular-is-rxjs-doomed-5b5dac574306 and my reply to it https://medium.com/p/451b872c21fa – maxime1992 May 31 '23 at 15:44
  • Thank you i actually only use signals where i had previously used a normal property in my html code. So

    Hi {{firstName}}

    becomes

    Hi {{firstName()}}

    . I did not use the async pipe a lot so i basically do not replace a single rxjs observable with signals.
    – Tilman Adam Jun 05 '23 at 13:52

1 Answers1

0

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.