Season Sale is LIVE! Get 40% off on all my products with code SEASONSALE Visit store!

Angular Signals + Firebase: Bye Bye RxJS?

4 min read


I built a simple contacts app recently with Angular signals and firebase and discovered that I didn’t actually need to use RxJS at all. The app does everything that a typical CRUD app should do - so this got me thinking - with signals do we even need RxJS anymore?

As with most things in life, the short answer is: it depends!

Video tutorial

If you're more of a video person, check out my video below. Otherwise, just keep reading :)

Quick overview of app

Let me first give you a quick rundown of my app. So my app has basically two components - one shows the contacts list and the other shows the add contact form.

Then I have the contacts service which basically encapsulates all of the data operations for the app.

So at the component level I’ve exclusively used signals to show data in the template and then the service basically just updates the signal for different operations. Here's some example code for the contacts-list component.

@Component({
  selector: "app-contacts-list",
  standalone: true,
  imports: [CommonModule, MatListModule, MatButtonModule, MatIconModule],
  template: `
    <mat-list>
      <mat-list-item *ngFor="let contact of contacts()"> ... </mat-list-item>
    </mat-list>
  `,
})
export class ContactsListComponent {
  contactsService = inject(ContactsService);
  contacts = this.contactsService.contacts;
}

And the service code is something like below.

@Injectable({
  providedIn: 'root',
})
export class ContactsService {
  contacts = signal<Contact[]>([...]);
}

No RxJS at component level, courtesy signals

As is apparent from above, at the component level, we’re not using any RxJS at all - no async pipe or observables because we don’t need it anymore.

But at the service level we’ll make API calls to fetch data and may need RxJS and observables. This is especially needed because network calls are asynchronous and signals are meant for synchronous reactivity.

But do we really need it even here? Turns out if you’re integrating with firebase and either using the JS SDK or even angular/fire v7, you’ll notice it has a promise-based API for most of its operations - along with some convenience functions which output observables.

So if you just use the promise based API functions for data operations, you don’t actually need RxJS anymore - at least for API calls.

Promise based fetching of data

So in the case of my app, my contacts service has a fetchContacts - which gets the contacts and sets our signal to the new value.

async fetchContacts() {
  const contacts = await getDocs(this.contactsCollection);
  this.contacts.set(contacts.docs.map((c) => ({ ...c.data(), id: c.id })));
}

And whenever we add or delete the contact with firebase, we just call this fetch function again - this refreshes the signal and thus the UI.

async addContact(newContact: Partial<Contact>) {
  ...
  await addDoc(this.contactsCollection, { ...newContact });
  await this.fetchContacts();
  ...
}

async deleteContact(id: string) {
  ...
  const docRef = doc(this.firestore, 'contacts', id);
  await deleteDoc(docRef);
  await this.fetchContacts();
  ...
}

So in case of firebase - the verdict is out and it is a YES!

You can very well create a fully functional Angular app without using any RxJS at all!

And this is good news for all those beginners who always find RxJS is a bit difficult to grasp, at least in the beginning of their Angular journey.

What if no firebase?

When you’re not using firebase however, and end up using some other REST API, you’ll likely want to use the HttpClientModule in Angular - which uses an RxJS based API. And then you’ll want to somehow convert your data from your observable to a signal.

A simple way to do that would be like here.

fetchContacts() {
  this.httpClient.get<Contact[]>('/contacts').subscribe(contacts => {
    this.contacts.set(contacts);
  })
}

But this is also where the rxjs-interop package comes in. Released in v16 as well, this provides for functions like toSignal and toObservable - meant to convert an observable to a signal and vice versa.

More on that in a future article :-)

Conclusion

For now, I can relish the fact that Angular has come a long way with the v16 release - and things are getting more and more simple for new developers.

The future looks bright!

The complete code for this app can be found on github here.

Thanks for reading.

Support

You may also like...