Effortless Web Storage Persistence in Angular Forms

Netanel Basal
Netanel Basal
Published in
3 min readMay 19, 2020

--

Imagine a case where a user fills out all or part of a form, and doesn’t get a chance to submit it. This could be caused by a sudden session timeout, or they hit the refresh button accidentally, or they momentarily navigated to a different page. Unless we’ve implemented persistence in our form values, such an experience can lead to a lot of frustration and even form abandonment.

In this article, we’re going to implement a directive that seamlessly lets us persist any form value in our application via our storage. Let’s start by implementing the storage part:

The Storage Implementation

There are multiple types of storage that browsers support. We have local and session storage, IndexedDB, and WebSQL. We don’t want to limit ourselves to a specific storage, so we’ll create a base provider that’ll be used as our token:

Even though the local and session storage APIs are synchronous, we want to be consistent and use the same response type for each. Let’s create a SessionStorage provider:

We’ve implemented the base StorageService provider and used the native session storage API. In our case, when we inject the StorageService provider, we want the SessionStorageService to be the default class that should be used. To do so, we’ll update the StorageService provider, adding the provider-definition key useClass:

Let’s move on and implement the directive. Later on, we’ll implement an additional storage type.

The Directive Implementation

Now that we’ve got storage to work with, we can create our directive. We want to only support forms that have the formGroup directive, and a name input, which will be used as the storage key:

Next, we need to obtain a reference to the FormGroupDirective instance. Luckily, we can easily do this by injecting the ControlContainer provider:

The reason it works is that Angular provides the FormGroupDirective under the ControlContainer token.

Let’s start with the straightforward part — updating the form with a stored value upon initialization. We check if the storage has a value for this form; If that’s the case, we update the form using the patchValue() method:

We want to update the storage with the form’s value only if the form is dirty, and in one of two cases — a refresh is made or an in-app navigation is performed.

The last piece is to remove the form value from the storage on submit:

Now we can use the directive in any form, as long as we remember to provide it with a name input:

Now let’s say we have a large form that we want to save in IndexedDB instead of session storage. First, we need to implement a provider for this storage type:

When working with storage APIs such as IndexedDB, we’re using localforage to make our lives easier. Now the only thing we need to do is to swap the implementation in the component we need:

Persist the Form’s Value Upon Change

If for some reason you have a use case where you need to persist the value upon any form value changes, you can do the following:

You can find the complete example here. Play with it in ng-run. If you want to take it and create a new open source project for ngneat, let me know.

🚀 In Case You Missed It

Here are a few of my open source projects:

  • Akita: State Management Tailored-Made for JS Applications
  • Spectator: A Powerful Tool to Simplify Your Angular Tests
  • Transloco: The Internationalization library Angular
  • Forms Manager: The Foundation for Proper Form Management in Angular
  • Cashew: A flexible and straightforward library that caches HTTP requests

And many more!

Follow me on Medium or Twitter to read more about Angular, Akita and JS!

--

--

A FrontEnd Tech Lead, blogger, and open source maintainer. The founder of ngneat, husband and father.