Oltre alle funzionalità di HTML5 che abbiamo già avuto modo di esplorare in questa guida, IE10 implementa anche le specifiche per la realizzazione di applicazioni Web offline.
Grazie ad ApplicationCache ed a IndexedDB possiamo creare applicazioni che lavorano ed archiviano dati in locale senza necessità di una connessione costante ad Internet. In questa lezione faremo una panoramica di queste tecnologie e vedremo qualche semplice esperimento con IE10.
ApplicationCache: personalizzare la cache
Sfruttando l'Application Cache possiamo chiedere al browser di memorizzare nella propria cache uno o più file che compongono la nostra pagina o la nostra applicazione Web.
È vero che il browser applica già una propria politica di caching, ma il criterio con cui viene applicata segue dei parametri dipendenti dallo specifico browser e dalle impostazioni dell'utente e segue una logica che potrebbe essere diversa da quella che noi possiamo ritenere adeguata per la nostra applicazione.
Avere la possibilità di indicare cosa mantenere in cache ed aggiornare le risorse della nostra applicazione quando lo riteniamo opportuno offre alcuni vantaggi:
- la possibilità di utilizzare l'applicazione in modalità offline;
- una maggiore velocità di accesso alle risorse dell'applicazione;
- una riduzione del carico di lavoro del server.
Ma vediamo come sfruttare in pratica questa funzionalità.
Per prima cosa creiamo un cache manifest, ovvero un file di testo in cui inserire l'elenco delle risorse della nostra applicazione da prendere in considerazione nella gestione della cache. Il contenuto può essere analogo al seguente:
CACHE MANIFEST
# versione 1.0
CACHE:
default.html
style.css
images/logo.png
images/background.png
scripts/script.js
NETWORK:
login.aspx
/api
FALLBACK:
*.html /offline.html
La prima riga del file di testo deve obbligatoriamente essere CACHE MANIFEST
. Seguono tre sezioni identificate dalle parole chiave CACHE
, NETWORK
e FALLBACK
.
Sezione | Descrizione |
---|---|
CACHE | indica quali risorse inserire in cache |
NETWORK | descrive le risorse che devono essere sempre recuperate online |
FALLBACK | specifica quale pagina visualizzare nel caso una risorsa online non sia disponibile |
Non è obbligatorio che siano presenti nel cache manifest tutte e tre le sezioni: è possibile creare un file minimale contenente le sole risorse da inserire in cache, mentre per i file non specificati IE10 si comporterà secondo la politica di caching corrente.
A questo punto dobbiamo semplicemente aggiungere un riferimento al cache manifest nel tag html delle pagine:
<html manifest="myManifest.appcache">
...
</html>
Un requisito importante è che il cache manifest deve essere inviato dal server con il mime-type text/cache-manifest.
Una volta ricevuto il manifest il browser gestirà le pagine come in esso specificato, ma come fare nel caso in cui decidiamo di aggiornare una risorsa che avevamo previsto di inserire in cache? Abbiamo due possibilità:
- aggiornare il cache manifest stesso, ad esempio modificando la riga di commento che contiene la versione corrente;
- aggiornare via JavaScript sfruttando l'oggetto ApplicationCache.
Il codice seguente mostra come verificare la disponibilità di una versione aggiornata della cache e di caricarla per sostituire quella corrente:
window.addEventListener('load', function(e) {
window.applicationCache.addEventListener('updateready', function(e) {
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
window.applicationCache.swapCache();
if (confirm('Aggiornamenti disponibili. Caricarli?')) {
window.location.reload();
}
}
}, false);
}, false);
Sfruttando lo stato dell'ApplicationCache e gli eventi ad esso associati abbiamo la possibilità di gestire in maniera puntuale la cache del browser in relazione alla nostra applicazione offline.
IndexedDB: gestire i dati
Un altro significativo contributo al potenziamento delle applicazioni Web lato client sia online che offline è offerto dal supporto a IndexedDB introdotto in IE10.
Questa tecnologia consente di memorizzare dati sul client e recuperarli in maniera efficiente come in una sorta di database locale. Non si tratta di un database relazionale, per cui non avremo SQL, tabelle, colonne e simili, ma di un Object Store, cioè di un sistema di persistenza di oggetti JavaScript.
IndexedDB è un sistema di persistenza di tipo transazionale, cioè ogni comando viene eseguito all'interno di una transazione, e prevede sia API sincrone che asincrone. IE10 supporta le API asincrone, pertanto le operazioni sull'Object Store non vengono eseguite immmediatamente ma restituiscono oggetti che rappresentano la richiesta su cui definire opportuni gestori di eventi.
Vediamo con un esempio come utilizzare IndexedDB per creare una semplice agenda personale. Definiamo innanzitutto il database:
var agenda = {};
var agenda.version = 1.0;
var agenda.open = function() {
var request = window.indexedDB.open("agenda", this.version);
request.onupgradeneeded = function(e) {
var db = e.target.result;
if(db.objectStoreNames.contains("appuntamenti")) {
db.deleteObjectStore("appuntamenti");
}
var store = db.createObjectStore("appuntamenti", {keyPath: "dataOra"});
}
request.onsuccess = function(e) {
agenda.db = e.target.result;
}
request.onerror = function(e) {
console.log("Si è verificato un errore nell'apertura del DB");
}
}
Per prima cosa abbiamo inizializzato un oggetto agenda
ed abbiamo impostato la proprietà version
. Come vedremo, la versione di un Object Store è fondamentale nella creazione e nella modifica della sua struttura.
All'oggetto agenda
assegniamo il metodo open()
che si occuperà di aprire ed eventualmente creare il nostro Object Store. Esso accede all'oggetto indexedDB
invocando il relativo metodo open()
con la stringa che rappresenta il nome del database e la versione corrente.
Il metodo open()
dell'oggetto indexedDB aprirà il database specificato se esiste la relativa versione oppure predispone la creazione o modifica della struttura del database.
Dal momento che, come abbiamo detto, le API sono asincrone, il risultato della chiamata al metodo open()
è un oggetto di tipo IDBRequest
sul quale definiremo dei gestori di evento.
In particolare nel nostro esempio gestiamo l'evento onupgradeneeded
, che si verifica quando il database non esiste o quando la sua versione è differente da quella specificata, l'evento onsuccess
, che si verifica in corrispondenza della corretta apertura del database, e l'evento onerror
in caso di errori.
Nel caso di creazione del database eliminiamo l'eventuale Object Store esistente di nome appuntamenti
e ne creiamo uno nuovo.
Nella creazione dell'Object Store abbiamo specificato il nome e un oggetto che indica la chiave primaria dello store, cioè la proprietà che identifica univocamente un oggetto nello store. Nel nostro caso abbiamo specificato la proprietà dataOra dell'oggetto appuntamento che popolerà la nostra agenda.
Vediamo ora come aggiungere un oggetto all'Object Store appuntamenti:
agenda.add = function(appuntamento) {
var db = agenda.db;
var trans = db.transaction(["appuntamenti"], "readwrite");
var store = trans.objectStore("appuntamenti");
var request = store.put({
"dataOra": appuntamento.dataOra,
"attivita": appuntamento.attivita
});
request.onsuccess = function(e) {
console.log("Appuntamento inserito correttamente!");
}
request.onerror = function(e) {
console.log("Si è verificato un errore nell'inserimento di un appuntamento!");
}
}
Come possiamo vedere, creiamo prima di tutto una transazione in lettura e scrittura sull'Object Store appuntamenti
. Dalla transazione otteniamo un riferimento all'Object Store di cui invochiamo il metodo put()
specificando l'oggetto da salvare. I gestori di evento permettono di gestire opportunamente l'esito dell'operazione.
Se desideriamo recuperare l'appuntamento per una specifica data e ora possiamo scrivere il seguente codice:
agenda.getAppuntamento = function(dataOra) {
var db = agenda.db;
var trans = db.transaction(["appuntamenti"], "readonly");
var store = trans.objectStore("appuntamenti");
var request = store.get(dataOra);
request.onsuccess = function(e) {
console.log("Attività trovata: " + e.target.result.attivita);
}
request.onerror = function(e) {
console.log("Si è verificato un errore nella ricerca dell'appuntamento");
}
}
Sempre partendo da una transazione sull'Object Store appuntamenti abbiamo utilizzato il metodo get()
dello store passando il valore della chiave in base alla quale recuperare l'appuntamento. Esistono vari modi di eseguire query che ci consentono di recuperare oggetti da un Object Store, ma il nostro obiettivo era semplicemente di dare un'idea della tecnologia indexedDB
supportata da IE10. Rimandiamo pertanto alla specifica per approfondimenti sull'argomento.
IDBExplorer: esplorare un database IndexedDB
Per venire incontro ai programmatori che intendono sperimentare IndexedDB, il team di sviluppo di Internet Explorer 10 ha realizzato un tool per l'esplorazione di un database di oggetti locali.
Il tool si chiama IDBExplorer ed è disponibile per il download gratuito. È scritto in JavaScript ed è un interessante esempio di utilizzo di IndexedDB. Una demo online mostra un esempio di applicazione che consente di gestire i "buoni propositi" per il nuovo anno e di navigare all'interno degli Object Store del database di oggetti. L'interfaccia utente si presenta come nella seguente figura:
Per utilizzare il tool in fase di debugging della nostra applicazione occorre richiamare la pagina IDBExplorer.html
in una nuova finestra di IE10 o in un iframe passando il nome del database.
Ad esempio, per esplorare in un iframe il database dell'agenda del nostro esempio dobbiamo scrivere il seguente codice:
<iframe src="IDBExplorer/IDBExplorer.html?name=agenda"></iframe>
Conclusioni
Il supporto ad ApplicationCache ed a IndexedDB introdotto in Internet Explorer 10 ci consente di realizzare applicazioni Web che possono eseguire offline attività articolate come la gestione di un database e sincronizzarsi con il server quando è necessario.
Questo approccio delinea un diverso modo di intendere le applicazioni Web: non più applicazioni che necessitano di una connessione Internet costante ma applicazioni che possono lavorare senza un dialogo continuo con il server. Un paradigma di elaborazione sempre più vicino al mondo mobile.