Angular Signals vs RxJS - Reuse It Effectively

In this video I want to write a service by using RxJS and then convert it to the signals so you can see the difference.

Signals is a new approach inside Angular and a lot of people consider that this is a full replacement of RxJS. Essentially it is not true but for some cases it is extremely easy to convert RxJS to signals and the code is much more readable and easier to support.

RxJS service

So we want to implement an input where you can type new users and they must be rendered on the screen when we submit the form.

Here we want to start with our RxJS service.

// src/app/users.service.ts
export class UsersService {
  private users$ = new BehaviorSubject<UserInterface>([])

  getUsers(): Observable<UserInterface[]> {
    return this.users$.asObservable()
  }

  addUser(user: UserInterface): void {
    const updatedUsers = [...this.users$.getValue(), user]
    this.users$.next(updatedUsers)
  }

  removeUser(userId: string): void {
    const updatedUsers = this.users$.getValue().filter(user => user.id !== userId)
    this.users$.next(updatedUsers)
  }
}

Here we created our UsersService. To store data inside we create a BehaviorSubject. But we made it private and exposed it only as an observable inside getUsers method.

Additionally we created addUser and removeUser which update our BehaviorSubject by using .next().

Now we must update our component to use this service for the input.

export class AppComponent {
  usersService = inject(UsersService);
  fb = inject(FormBuilder);
  addForm = this.fb.nonNullable.group({
    name: '',
  });
  users$ = this.usersService.getUsers();
}

Here we injected UsersService and saved a users stream in a property. Now we can render our users in the template.

<form [formGroup]="addForm" (ngSubmit)="onUserAdd()">
  <input type="text" formControlName="name" />
</form>

<div *ngFor="let user of users$ | async">
  {{ user.name }}
  <span (click)="removeUser(user.id)">X</span>
</div>

We rendered our list of users with ngFor and added removeUser click event which must remove a user.

Now let's add this function and update our onUserAdd function.

onUserAdd(): void {
  const user: UserInterface = {
    id: Math.random().toString(),
    name: this.addForm.getRawValue().name,
  };
  this.usersService.addUser(user);
  this.addForm.reset();
}

removeUser(userId: string): void {
  this.usersService.removeUser(userId);
}

In both functions we just call our service and provide some data inside.

Working project

As you can see we can add and remove users to our list.

Using Angular signals

Now let's refactor this service to Angular signals.

export class UsersService {
  private usersSig = signal<UserInterface[]>([]);

  getUsers(): Signal<UserInterface[]> {
    return computed(this.usersSig);
  }

  addUser(user: UserInterface): void {
    this.usersSig.update((users) => [...users, user]);
  }

  removeUser(userId: string): void {
    const updatedUsers = this.usersSig().filter((user) => user.id !== userId);
    this.usersSig.set(updatedUsers);
  }
}

It looks really similar but now we store our list in a signal. We also return a computed signal to make it readonly in getUsers. Our addUser and removeUser functions use .update to change the list of users.

Now let's make changes to our component.

usersSig = this.usersService.getUsers();

We just need to update a single line which returns a signal now.

<div *ngFor="let user of usersSig()">
  {{ user.name }}
  <span (click)="removeUser(user.id)">X</span>
</div>

And to render a list of users to don't use an async pipe anymore but we read a signal value.

Working project

As you can see our project work just like before but now we built everything with signals.

Want to conquer your next JavaScript interview? Download my FREE PDF - Pass Your JS Interview with Confidence and start preparing for success today!

📚 Source code of what we've done
Did you like my post? Share it with friends!
Don't miss a thing!
Follow me on Youtube, Twitter or Instagram.
Oleksandr Kocherhin
Oleksandr Kocherhin is a full-stack developer with a passion for learning and sharing knowledge on Monsterlessons Academy and on his YouTube channel. With around 15 years of programming experience and nearly 9 years of teaching, he has a deep understanding of both disciplines. He believes in learning by doing, a philosophy that is reflected in every course he teaches. He loves exploring new web and mobile technologies, and his courses are designed to give students an edge in the fast-moving tech industry.