Browser Storage and BehaviorSubject: Achieving Consistent State in Angular Through Storage Decorators
Tags: angular, decorators, rxjs, behaviorsubject
Published: 2024-06-18 12:01:22

Managing data persistence in Angular applications can be a hassle, especially when you need to keep your application state synchronized with local or session storage. In this post I will provide a set of property decorators that bind your storage values to `BehaviorSubject`s, ensuring that your state remains consistent with the browser's storage.

> After test in production, I might consider publishing a library with them, but feel free to use the code as you wish. https://github.com/saulodias/ngx-storage

## Why Use These Decorators?

They simplify the process of storing and retrieving data from local and session storage. They handle the heavy lifting for you.

With them you can:

- Automatically sync your state with browser storage.
- Use `BehaviorSubject` for reactive state management.
- Easily revert to a fallback state when necessary.

## How It Works

### LocalStorage

The `LocalStorage` decorator allows you to store data in local storage effortlessly.
```typescript
import { LocalStorage } from 'ngx-storage';
import { BehaviorSubject } from 'rxjs';

class TestClass {
	@LocalStorage('key', 'defaultValue')
	public localStorageValue!: BehaviorSubject<string | null>;
}
```

### SessionStorage

The `SessionStorage` decorator lets you store data in session storage just as easily.

```typescript
import { SessionStorage } from 'ngx-storage';
import { BehaviorSubject } from 'rxjs';

class TestClass {
	@SessionStorage('key', 'defaultValue')
	public sessionStorageValue!: BehaviorSubject<string | null>;
}
```

### Custom JSON Converter

For more complex types, you can provide a custom JSON converter.
```typescript
import { JsonConverter, SessionStorage } from 'ng-storage';
import { BehaviorSubject } from 'rxjs';

type CustomType = { date: Date };
type CustomTypeStored = { date: number };

const customConverter: JsonConverter<CustomType, CustomTypeStored> = {
	toJson: (object) => ({ date: object.date.getTime() }),
	fromJson: (object) => ({ date: new Date(object.date) }),
};

class TestClass {
	@SessionStorage('key', { date: new Date() }, customConverter)
	public sessionStorageValue!: BehaviorSubject<CustomType | null>;
}
```
### Using NgxStorage in an Angular Service

Here's an example of how to use these decorators in an Angular service:
```typescript
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { LocalStorage, SessionStorage } from 'ngx-storage';

@Injectable({
	providedIn: 'root',
})
export class StorageService {
	@LocalStorage('localKey', 'defaultLocalValue')
	public localStorageValue!: BehaviorSubject<string | null>;

	@SessionStorage('sessionKey', 'defaultSessionValue')
	public sessionStorageValue!: BehaviorSubject<string | null>;

	constructor() {
		// Subscribe to localStorageValue
		this.localStorageValue.subscribe(value => {
			console.log('LocalStorage value:', value);
		});

		// Set a new value to localStorageValue
		this.localStorageValue.next('newLocalValue');

		// Subscribe to sessionStorageValue
		this.sessionStorageValue.subscribe(value => {
			console.log('SessionStorage value:', value);
		});

		// Set a new value to sessionStorageValue
		this.sessionStorageValue.next('newSessionValue');
	}
}
```

### Using the Service in a Component
```typescript
import { Component, OnInit } from '@angular/core';
import { StorageService } from './storage.service';

@Component({
	selector: 'app-storage-example',
	templateUrl: './storage-example.component.html',
	styleUrls: ['./storage-example.component.css']
})
export class StorageExampleComponent implements OnInit {

	constructor(private storageService: StorageService) {}

	ngOnInit() {
		// Access and use the storage service properties as needed
		console.log('LocalStorage Initial:', this.storageService.localStorageValue.value);
		console.log('SessionStorage Initial:', this.storageService.sessionStorageValue.value);

		// Update the values
		this.storageService.localStorageValue.next('updatedLocalValue');
		this.storageService.sessionStorageValue.next('updatedSessionValue');
	}
}
```

### Setting Fallback State

When `null` is passed to `.next()`, the fallback state is set instead.

```typescript
this.localStorageValue.next(null); // Reverts to 'defaultLocalValue'
this.sessionStorageValue.next(null); // Reverts to 'defaultSessionValue'
```

> Note that this is beta and not published, but you can use the code at you wish, for commercial and personal purposes. It's provided as it is though, with no guarantee.