IndexedDB con ExtJS
Tags: extjs, indexeddb, javascript
Published: 2024-05-06 07:34:23


La possibilità di **salvare informazioni** localmente sul browser è una funzionalita molto utile che si è evoluta nel tempo. Ovviamente sul browser, per motivi di sicurezza, non è possibile scrivere e leggere a piacimento sul file system dell'utente. Ma tramite diversi meccanismi è comunque possibile memorizzare delle informazioni in maniera persistente.

In questo articolo vedremo diversi tipi di **storage** e come utilizzarli con [ExtJS](https://www.sencha.com/products/extjs/). In particolare analizzeremo l'implementazione di **IndexedDB** offerta dalla libreria [(N)ext](https://github.com/lminuti/next).

## Cookie

Il modo "classico" di salvare dati sul browser è tramite i *cookie* che permettono proprio di salvare **piccole stringhe** da recuperare in un secondo tempo dal server oppure direttamente sul browser tramite codice [JavaScript](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies). Più recentemente sono strati introdotti dei veri e propri database: **LocalStorage**, **SessionStorage** e **IndexexDB**.

![Cookie](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vfmvn46zgabs1b2zjtru.png)

## LocalStorage e SessioneStorage

*localStorage* e *SessionStorage* sono due semplici **database chiave/valore** presenti nel browser. In pratica per ogni "origin" (host+porta) è possibile associare una serie chiavi a dei valori in modo da poterli recuperare in un secondo tempo.

![LocalStorage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o10ln8m30rpd8olyj77a.png)

La differenza tra i due è che mentre il *LocalStorage* è permanente, i dati contenuti nel *SessionStorage* vengono eliminati alla chiusura del browser. 

### Limitazioni

I problemi principali di questi tipi di storage sono i seguenti:

* **Le dimensioni**: ci sono differenze tra i vari browser ma in generale non è possibile salvare più di 5MB di dati
* **L'API è sincrona**, quindi se la dimensione dei dati è consistente o si eseguono molte operazioni di lettura/scrittura si rischia di "freezare" il browser.
* Gli elementi vengono **salvati come stringa** (eventualmente chiamando il metodo `toString()`), di conseguenza per gli oggetti più complessi è necessario eseguire una conversione manualmente.

### ExtJS

In *ExtJS* ci sono varie classi che fanno uso di questi tipi di storage:

* [Ext.util.LocalStorage](https://docs.sencha.com/extjs/7.7.0/modern/Ext.util.LocalStorage.html): Questa classe è un semplice wrapper della classe *localStorage* nativa del browser. Semplicemente si limita a salvare i dati localmente nel caso in cui il browser non supporti *localStorage*.
* [Ext.state.LocalStorage](https://docs.sencha.com/extjs/7.7.0/modern/Ext.state.LocalStorage.html): Questa classe serve a salvare lo stato dei componenti (dimensione, posizione, ecc.) in modo da ripresentarli ad una successiva apertura dell'applicazione (vedi [Ext.Stateful](https://docs.sencha.com/extjs/7.7.0/modern/Ext.Stateful.html)).
* [Ext.data.proxy.LocalStorage](https://docs.sencha.com/extjs/7.7.0/modern/Ext.data.proxy.LocalStorage.html): Questo proxy, collegato ad uno store, permette di leggere e scrivere dati dal *localStorage*.
* [Ext.data.proxy.SessionStorage](https://docs.sencha.com/extjs/7.7.0/modern/Ext.data.proxy.SessionStorage.html): Esattamente come il precedente ma legge e scrive sul *SessionStorage*.

## IndexedDB

Per ovviare ai problemi di LocalStorage da qualche tempo su tutti i principali browser desktop e mobili (vedi [browser supportati](https://caniuse.com/?search=indexeddb)) è possibile usare **IndexedDB**. A differenza di LocalStorage consente alle applicazioni web di memorizzare grandi quantità di dati strutturati, offrendo un'interfaccia di accesso flessibile e basata sulle [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises).

### funzionalità principali

* **Modello di dati**: IndexedDB supporta la memorizzazione di oggetti JavaScript complessi, consentendo la memorizzazione di dati strutturati in modo gerarchico.
* **Dimensione dei dati**: IndexedDB gestisce grandi quantità di dati in modo più efficiente rispetto a localStorage.
* **Interfaccia di accesso**: IndexedDB fornisce un'interfaccia di programmazione più complessa rispetto a localStorage. Richiede la comprensione di concetti come transazioni, cursori e indici per manipolare i dati in modo efficiente.
* **Supporto per transazioni**: IndexedDB supporta le transazioni, consentendo operazioni atomiche su più record di dati.
* **Performance**: IndexedDB è progettato per gestire grandi quantità di dati in modo efficiente, offrendo prestazioni migliori rispetto a localStorage.

Il difetto principale di *IndexedDB* è proprio la **complessità dell'API**. In JavaScript esistono diverse librerie che cercano di semplificarne l'uso, per esempio:

* [IDB-Keyval](https://github.com/jakearchibald/idb-keyval)
* [Dexie.js](https://github.com/dexie/Dexie.js)

In particolare *IDB-Keyval* offre un'**API estremamente semplificata** che ricorda molto quella di LocalStorage. Questa semplificazione chiaramente ha delle conseguenze e infatti alcune caratteristiche avanzate di IndexedDB (come l'uso delle transazioni) vengono meno. Ma per chi viene da LocalStorage e ha bisogno semplicemente di più performance e spazio di archiviazione sicuramente è una scelta interessante.

## ExtJS - (N)ext

Purtroppo in *ExtJS* non c'è nessun supporto nativo per *IndexedDB*. Ed è per questo che ho deciso di aggiungerlo nella libreria *(N)ext* che ho recentemente pubblicato su [GitHub](https://github.com/lminuti/next). 

Con *(N)ext* è possibile connettersi ad un database in questo modo:

```javascript
// If you add a new store in the schema you must increment the database version!
database = await Next.IndexedDB.open('dbname', {
    version: 1,
    stores: ['store1','store2', 'store3', 'store4']
});
```

Se il database non esiste verrà creato con i 4 *store* (che non hanno niente a che fare con gli store di ExtJS), che si possono considerare come delle tabelle. Per come funziona *IndexedDB* è importante ricordarsi che qualora serva uno store addizionale è necessario incrementare il numero di versione.

È importante notare che l'apertura del database (come praticamente tutte le altre API) è asincrona. Infatti nell'esempio viene chiamata usando `await`. 

![IndexedDB](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rpjcv2325pv9htlkyh6f.png)

Una volta ottenuto un riferimento al database è possibile interagire con gli store. La libreria offre diverse possibilità, di seguito sono elencate le principali.

**Inserimento/modifica di un elemento sullo store**
```javascript
await database.store1.set(5, {
    name: 'Charlie'
});
```
Come si può vedere dall'esempio il valore è un oggetto e, in questo caso, non c'è bisogno di nessuna trasformazione.

**Lettura di un valore**
```javascript
const value = await database.store1.get(5);
```

Anche in questo caso la variabile `value` viene valorizzata direttamente con l'oggetto.

**Lettura di tutte le chiavi**

```javascript
const keys = await database.store1.keys();
```

Questo metodo restituisce in *array* con tutte le chiavi dello store.

**Lettura di tutti i valori**

```javascript
const values = await database.store1.values();
```

Questo metodo restituisce in *array* con tutti i valori dello store.

**Eliminazione di un valore**
```javascript
await database.store1.del(5);
```

Il metodo `del` elimina l'elemento indicato dalla chiave.

**Eliminazione di tutto lo store**

```javascript
await database.store1.clear();
```

Questo metodo svuota l'intero store.

## Conclusioni

In questo articolo abbiamo visto i principali metodi per salvare informazioni persistenti sul browser in particolare con **IndexedDB**. Ci siamo poi soffermati ad analizzare quanto offerto dalla libreria [GitHub](https://github.com/lminuti/next).

Vi invito a provarla e farmi sapere le vostre impressioni!