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.
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.
As you can see our project work just like before but now we built everything with signals.
Want to learn Angular Signals and build a practical application? Check out my Angular Signals - Building Quiz Angular Project. This course will guide you through creating a real-world quiz app, helping you master Angular Signals along the way.
📚 Source code of what we've done