Account Management using Cloud Firestore in Angular

Extending your authentication process with user settings

Lucio Francisco
Sep 25, 2019 · 5 min read

This is the second article being part of a series exploring new ways of developing all the core building blocks of a Single Page Application with Angular and Firebase.

< Go to the previous article

Once you got the authentication process covered, you’ll face the need of giving your users a profile they can edit and share publicly (at least part of it) together with the possibility of managing their account including permanently deleting it.

Following up on the AuthService described in the previous article of this series, in this article we’ll explore a way of storing a customizable user profile in Cloud Firestore to be easily accessed across the application.

Key Ingredients

Here we’ll be covering:

  1. DatabaseService: the database service built on AngularFirestore service from AngularFire.

As for the previous article, all of the above will be part of the live demo available on StackBlitz you’ll find the reference of at the bottom of this article.

Database Service

As you may remember from the previous article, I support the approach of implementing our own core services, so, to better serve our application needs and that’s what we are going to do with our DatabaseService:

Database Service

Our service mimics AngularFirestore but instead of simply wrapping it, this service gets down to the original firestore api, so, to introduce additional features such as batch writes and transactions we’ll be needing in the upcoming articles.

Besides, I’m a huge fan of AngularFire library and the way they implemented Observable based data synchronization; I still prefer to rely on the original Promise based firestore api when it comes to perform “single pointed” operations like get(), delete() and so forth.

So the basic idea here, is that our implementation is going to pick the best of both worlds :)

The most relevant function, for what concerns the subject of this article, is document() returning an instance of a document object within the database.

Database Document

The DatabaseDocument class looks like purely wrapping the DocumentReference (here renamed dbDocumentRef) from the firestore api:

Database Document class

But don’t get fooled… we are not reinventing the wheel. By looking closely to the stream() method you’ll see we are actually taking advantage from AngularFirestoreDocument snapshotChanges() method to stream the document content as it changes in the database.

The original observable content is than mapped into the expected data format. In fact, as you may have noticed, DatabaseDocument is a Generic class using the T argument to type the document content with an interface of your choice extending dbCommom.

The dbCommon interface is the basic document data type we use to “spice up” the basic functionalities of DatabaseDocument so that every time we write into the database the document automatically gets a created or updated timestamp for calling set() or update() method respectively.

An additional upsert() method is actually checking for the document existence prior to set or update the content consequently.

Finally, everytime we read the document content we get to read the id as well, so, we’ll always have the document uid at our disposal.

User Profile

The key piece of this article is the UserProfile service extending the DatabaseDocument functionalities to embody the authenticated user profile:

User Profile service

As you see from the code above, UserProfile takes advantage from all the functionalities implemented by DatabaseDocument.

The stream() method is overridden to resolve the authenticated user first, so, to stream the profile content from a document named as the user uid.

In the constructor the base class is initialized with a null reference than replaced by the effective document reference by stream() as soon as the current user uid is known.

A snapshot of the profile content is persisted in the variable data making sure the document reference is always up to date.

Finally, there’s an helper method register() to initialize the profile content with the user object account information when a new user is registered.

Settings Page

For the settings page I decided to go with children routes, so, the main SettingsComponent page is pretty simple letting the user navigate between the ProfileComponent and the AccountComponent or to eventually sign-out:

Settings Component

So you see the component implements a MatNavList on the left rendering the child component on the right while it directly calls the AuthGuard disconnect() method to sign-out.

Routes

The specific routes are than defined in SettingsModule:

So that ProfileComponent or AccountComponent is rendered accordingly in the child router-outlet.

Profile Component

The profile page displays the profile data in a form for the user to modify it:

User Profile page

Is in the constructor of the ProfileComponent where the form is built and its content patched loading it from the profile:

User Profile component

While thesave() method is the one called to update the profile marking the form back to pristine when done.

Account Component

The second page let the user manage the account functionalities:

User Account page

The component purely implements UI functionalities relying on the AuthGuard service to perform the task behind the scenes:

User Account component

Particularly, since updating the account credentials or deleting the account completely requires the user to have signed in recently, this implementation uses AuthGuard to prompt the user for re-authentication providing both a convenient way to ask for confirmation and to refresh the authentication token.

Updated Login Component

Since we now extended the demo app with UserProfile we have to make sure a profile is created every time a new user is registered:

Updated registerNew() in Login component

So the LoginComponent registerNew() method has been updated accordingly.

Similarly, prior to delete the user, we better delete her profile document as well:

And that’s the perfect task to be done from within deleteAccount() method.

Conclusions

At this point in our journey we are almost done with user management. A last piece of this puzzle would be enabling our users uploading their own profile picture. We’ll see how this can be implemented in the next article of this series.

Resources

Most of the code used for this article comes from wizdm.io, an open source project hosted on github. Feel free to reach me out at hello@wizdm.io.

A fully functional demo of the app implementing the code described here is available live on StackBlitz here:

Live demo on StackBlitz

Wizdm Genesys

An attempt in combining modern software development with ancient wisdom

Lucio Francisco

Written by

I believe that whatever problem we’re puzzling ourselves with, once we really get to the bottom of it the solution has to be simple

Wizdm Genesys

An attempt in combining modern software development with ancient wisdom

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade