Press enter or click to view image in full size

Today, let’s look at an interesting way to use Angular Signals to automatically synchronize the state of an application in the browser’s localStorage.

Our example is a simple dropdown to select the app language. When the user changes that language, we want the information to persist in localStorage:

First, we can create a service to interact with localStorage.

@Injectable({
providedIn: 'root'
})
export class StorageService {

setValue(key: string, value: T) {
localStorage.setItem(key, JSON.stringify(value));
}

getValue(key: string, defaultValue: T): T {
let value = localStorage.getItem(key);
return value ? JSON.parse(value) : defaultValue;
};

}

Note the usage of generics for improved type safety. That way, we can inject that service and provide the type information of what we expect to store as follows:

storage = inject(StorageService);

Our next step is to have a language service to store the current language:

@Injectable({
providedIn: 'root'
})
export class LanguageService {
// The localStorage key we want to use
private readonly LANG_KEY = "lang";
// Let's inject our StorageService
private storage = inject(StorageService);
// We create a public signal initialized with the current value
// in localStorage, or "en_uk" if no value stored yet
language = signal(this.storage.getValue(this.LANG_KEY, "en_uk"));

}

With the above service, since our signal is public and writable, we can use it as follows in our component:

export class App {
language = inject(LanguageService).language;
}

And in my template, I can bind the dropdown value to that signal using ngModel:

   

With that code in place, any update to the dropdown selection automatically updates the signal value (thanks to ngModel’s seamless integration with Angular Signals). The default signal value is also the default dropdown selection.

Get Alain Chautard’s stories in your inbox

Join Medium for free to get updates from this writer.

How to sync that value in localStorage then? Well, thanks to the beauty of Angular Signals, we’re just one line of code away from that auto-sync.

Let’s update our LanguageService by adding an effect to it:

export class LanguageService {

private readonly LANG_KEY = "lang";
private storage = inject(StorageService);
language = signal(this.storage.getValue(this.LANG_KEY, "en_uk"));

// The magic of effects does all the work
private autoStorageSync = effect(() =>
this.storage.setValue(this.LANG_KEY, this.language()));
}

Since the value of language() is always up-to-date, I create an effect that stores the value of language() into localStorage. Since reading a signal in an effect makes it a dependency of that effect, any change of language() triggers the effect function, thus updating localStorage automatically!

Note my syntax trick to avoid adding a constructor: I assign the effect to a private class property. I never use that property, but its name acts as extra documentation to make the purpose of my effect self-explanatory:

 private autoStorageSync = effect(() => 
this.storage.setValue(this.LANG_KEY, this.language()));

And that is how we can implement an elegant signal-based solution to sync data to localStorage with very little effort. You can find the entire example in action on Stackblitz.

My name is Alain Chautard. I am a Google Developer Expert in Angular and a consultant and trainer at Angular Training, where I help web development teams learn and become proficient with Angular / React / JavaScript.

If you need any help learning web technologies, feel free to get in touch!

If you enjoyed this article, please clap for it or share it. Your help is always appreciated. You can also subscribe to my articles and YouTube video page.

Share.
Leave A Reply