AutoSave für AJAX-Formulare
Timo Holzherr
AJAX in ACTION 2007, 06.11.2007, Frankfurt
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Timo Holzherr
Web Application Developer bei der Nero AG / Karlsbad
Web-Community My Nero (PHP5, AJAX)
Online Services für verschiedene Nero-Applikationen
Lehrauftrag bei der Berufsakademie Stuttgart
Vorlesung Software-Engineering 2
Betreuung von Studienarbeiten, Thema Compiler-Entwicklung
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Agenda
Was ist AutoSave?
Praktische Anwendungen
Formulareingaben überwachen
Zeitintervalle steuern, Eingaben zählen
AJAX-Request absenden
Formular-Validierung
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Was ist AutoSave?
Auf AJAX basierendes Feature
Für HTML-Formulare
Automatisches Speichern der Benutzer-Eingaben
Eingaben überwachen
Änderungen regelmäßig zum Server übertragen
Recovery-Feature
Browser-Absturz
Versehentliches Schließen des Browserfensters
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
AutoSave – Praktische Anwendungen
Vor AJAX nur in Desktop-Applikationen
Microsoft Office
…
Heute bei vielen Online-Services
My Nero (http://my.nero.com/)
Google Mail (http://mail.google.com/)
…
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
AUTOSAVE BEI MY NERO
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
AutoSave bei My Nero
Blog-Editor
Regelmäßiges
Speichern im
Hintergrund
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
AutoSave bei My Nero
Recovery-Feature auf dem Login-Screen
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
AUTOSAVE BEI GOOGLE MAIL
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
AutoSave bei Google Mail
Während der Eingabe der E-
Mail:
Regelmäßiges Speichern der
Eingaben
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
AutoSave bei Google Mail
Der Benutzer findet die automatisch gespeicherte E-Mail im Ordner Entwürfe
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Entwicklung eines AutoSave-Frameworks
Trennung der Aufgaben
i. Formulareingaben überwachen
ii. Zeitintervalle steuern
iii. Formulardaten absenden
Verschiedene Klassen
i. FormObservable.js
ii. AutoSave.js
iii. Note.js
Vorteil der Trennung
Komponenten können einzeln verwendet werden
Bsp.: Live-Validierung von Formularen durch FormObservable
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Formulareingaben überwachen
Eingaben des Benutzers müssen überwacht werden
Registrieren für Events aller Form-Elemente
onkeyup (Nach einem Tastendruck)
onmouseup (Nach einem Mausklick)
onblur (Verlassen eines Formularfeldes)
onchange (Ändern einer Check- oder Dropdown-Box)
Beim Auftreten dieser Events
Prüfen, ob sich die Formularwerte geändert haben
Mithilfe des Observer-Patterns Änderungen signalisieren
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse FormObservable
Speichert die Liste
der Observer
Initialisiert die Klasse an einer
Form-Node
Sendet ein Event an alle
ObserverFügt einen
Observer hinzu
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse FormObservable
var observable = new FormObservable( document.forms[0] ); observable.addObserver( { onupdate: function() { alert(“Form element has been updated.“); } } );
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse FormObservable: Initialisierung
FormObservable = function( formNode ) { var events = [ 'keyup', 'mouseup', 'blur', 'change' ];
var call = this._onFieldChange.bind( this );
for( var i=0; i < formNode.elements.length; i++ ) { var elem = formNode.elements.item( i ); for( var j=0; j < events.length; j++ ) { elem.addEventListener( events[j], call, true ); } } }
Der Konstruktor erwartet ein HTML-
Form-Objekt als Parameter
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse FormObservable: Initialisierung
FormObservable = function( formNode ) { var events = [ 'keyup', 'mouseup', 'blur', 'change' ];
var call = this._onFieldChange.bind( this );
for( var i=0; i < formNode.elements.length; i++ ) { var elem = formNode.elements.item( i ); for( var j=0; j < events.length; j++ ) { elem.addEventListener( events[j], call, true ); } } }
Events, die überwacht
werden sollen
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse FormObservable: Initialisierung
FormObservable = function( formNode ) { var events = [ 'keyup', 'mouseup', 'blur', 'change' ];
var call = this._onFieldChange.bind( this );
for( var i=0; i < formNode.elements.length; i++ ) { var elem = formNode.elements.item( i ); for( var j=0; j < events.length; j++ ) { elem.addEventListener( events[j], call, true ); } } }
Methode, die später die
Events verarbeitet
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse FormObservable: Initialisierung
FormObservable = function( formNode ) { var events = [ 'keyup', 'mouseup', 'blur', 'change' ];
var call = this._onFieldChange.bind( this );
for( var i=0; i < formNode.elements.length; i++ ) { var elem = formNode.elements.item( i ); for( var j=0; j < events.length; j++ ) { elem.addEventListener( events[j], call, true ); } } }
bind: Methode aus der Bibliothek Prototype [1] Setzt den Kontext der Methode auf die Instanz thisJS-Event-Handling: Ansonsten Verlust des Kontextes
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse FormObservable: Initialisierung
FormObservable = function( formNode ) { var events = [ 'keyup', 'mouseup', 'blur', 'change' ];
var call = this._onFieldChange.bind( this );
for( var i=0; i < formNode.elements.length; i++ ) { var elem = formNode.elements.item( i ); for( var j=0; j < events.length; j++ ) { elem.addEventListener( events[j], call, true ); } } }
Jedem Formular-
Element wird call als Event-
Listener hinzugefügt
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse FormObservable: Initialisierung
FormObservable = function( formNode ) { var events = [ 'keyup', 'mouseup', 'blur', 'change' ];
var call = this._onFieldChange.bind( this );
for( var i=0; i < formNode.elements.length; i++ ) { var elem = formNode.elements.item( i ); for( var j=0; j < events.length; j++ ) { elem.addEventListener( events[j], call, true ); } } }
Hier: Event-Implementierung nur für Mozilla Firefox
Empfohlen: Bibliothek für Browser-Abstraktion
Bsp.: Prototype [1]
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse FormObservable: _onFieldChange
FormObservable.prototype._onFieldChange = function( ) { … for( var i=0; i < formNode.elements.length; i++ ) { var elem = formNode.elements.item(i);
var oldVal = this._getStoredValue( elem ); var newVal = elem.value;
if( newVal != oldVal ) { this.notifyObservers( 'update' ); this._setStoredValue( elem, newVal ); } } }
Wird vom JS-Event-Handling aufgerufen, wenn eines der überwachten Ereignisse eintritt
• keyup
• mouseup
• blur
• change
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse FormObservable: _onFieldChange
FormObservable.prototype._onFieldChange = function( ) { … for( var i=0; i < formNode.elements.length; i++ ) { var elem = formNode.elements.item(i);
var oldVal = this._getStoredValue( elem ); var newVal = elem.value;
if( newVal != oldVal ) { this.notifyObservers( 'update' ); this._setStoredValue( elem, newVal ); } } }
Dabei wird jedes Formularelement auf eine
Änderung geprüft
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse FormObservable: _onFieldChange
FormObservable.prototype._onFieldChange = function( ) { … for( var i=0; i < formNode.elements.length; i++ ) { var elem = formNode.elements.item(i);
var oldVal = this._getStoredValue( elem ); var newVal = elem.value;
if( newVal != oldVal ) { this.notifyObservers( 'update' ); this._setStoredValue( elem, newVal ); } } }
FormObservable:
Merkt sich lokal für jedes Element den letzten Wert
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse FormObservable: _onFieldChange
FormObservable.prototype._onFieldChange = function( ) { … for( var i=0; i < formNode.elements.length; i++ ) { var elem = formNode.elements.item(i);
var oldVal = this._getStoredValue( elem ); var newVal = elem.value;
if( newVal != oldVal ) { this.notifyObservers( 'update' ); this._setStoredValue( elem, newVal ); } } }
Observers über eine Änderung informieren
Wertänderung lokal merken
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse FormObservable: Default Values
Problem
Formularelemente mit Default-Values
Beim ersten Klick würde das System vermuten, dass sich
Elemente mit Default-Values geändert haben
Abhilfe
Default-Values zu Beginn als Startwert merken
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse FormObservable: _loadDefaultValues
FormObservable = function( formNode ) { … // Initialisierung der Event-Handler this._loadDefaultValues(); }
Nach der bereits erwähnten Initialisierung:
Laden der Default Values
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse FormObservable: _loadDefaultValues
FormObservable.prototype._loadDefaultValues = function( ) { for( var i=0; i < this.form.elements.length; i++ ) { var elem = this.form.elements.item(i); this._setStoredValue( elem, elem.value ); } };
FormObservable = function( formNode ) { … // Initialisierung der Event-Handler this._loadDefaultValues(); }
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
BEISPIEL OBSERVABLE [2]
var observable = new FormObservable( document.forms[0] ); observable.addObserver( { onupdate: function() { alert(“Form element has been updated.“); } } );
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Observer-Notifications empfangen
Empfangen der Observer-Notifications
AJAX-Request muss an FormObservable gekoppelt werden
Ansatz:
var observable = new FormObservable( document.forms[0] ); observable.addObserver( { onupdate: function() { // AJAX Request senden } } );
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Observer-Notifications empfangen
Problem
Bei jedem Tastaturanschlag Übertragung der Änderung
Viele HTTP-Requests
Überlastung des Web-Servers
Lösung
Zeitverzögertes Senden der Daten
Einstellbares Timeout
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse AutoSave
Hält eine Instanz der Klasse FormObservable
vorInstanziert die Klasse an einer Form-Node,
Timeout konfigurierbar
Fügt einen Observer hinzu, delegiert FormObservable
Setzt alle Timeouts zurück
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse AutoSave
var as = new AutoSave( document.forms[0], 5 );
as.addObserver( { ontimeout: function(){ /* AJAX Request senden */ }, onupdate: function() { /* aktivieren */ } } );
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse AutoSave: Initialisierung
AutoSave = function( form, saveTime ) { this._options = { form: form, saveTime: saveTime * 1000, // convert into milliseconds }; this._interval = null;
this._observable = new FormObservable( form ); this._observable.addObserver( { onupdate: this._updateInterval.bind( this ), ontimeout: this.reset.bind( this ) } );};
Die übergebenen Parameter werden gespeichert
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse AutoSave: Initialisierung
AutoSave = function( form, saveTime ) { this._options = { form: form, saveTime: saveTime * 1000, // convert into milliseconds }; this._interval = null;
this._observable = new FormObservable( form ); this._observable.addObserver( { onupdate: this._updateInterval.bind( this ), ontimeout: this.reset.bind( this ) } );};
Klassenvariable _interval wird initialisiert.
Referenziert Timeout, um es wieder zu stoppen var to = window.setTimeout( callback, zeit );
window.clearTimeout( to );
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse AutoSave: Initialisierung
AutoSave = function( form, saveTime ) { this._options = { form: form, saveTime: saveTime * 1000, // convert into milliseconds }; this._interval = null;
this._observable = new FormObservable( form ); this._observable.addObserver( { onupdate: this._updateInterval.bind( this ), ontimeout: this.reset.bind( this ) } );};
FormObservable instanzieren
und Observer hinzufügen
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse AutoSave: _updateInterval
AutoSave.prototype._updateInterval = function( ) { this._resetInterval(); this._interval = window.setTimeout( this._timeExceeded.bind( this ), this._options.saveTime ); };
Wird bei einem Update-Event
des FormObservable aufgerufen
(Änderungen am Formular)
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse AutoSave: _updateInterval
AutoSave.prototype._updateInterval = function( ) { this._resetInterval(); this._interval = window.setTimeout( this._timeExceeded.bind( this ), this._options.saveTime ); }; AutoSave.prototype._resetInterval = function()
{ if( this._interval ) { window.clearTimeout( this._interval ); this._interval = null; } }
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse AutoSave: _updateInterval
AutoSave.prototype._updateInterval = function( ) { this._resetInterval(); this._interval = window.setTimeout( this._timeExceeded.bind( this ), this._options.saveTime ); };
Aufgerufene Methode: AutoSave._timeExceeded()• Setzt this._interval zurück• Löst mit notifyObservers das Event „timeout“ aus
Timeout starten
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
BEISPIEL AUTOSAVE[2]
var as = new AutoSave( document.forms[0] ); as.addObserver( { onupdate: function() { document.forms[0].draft.disabled = null; }, ontimeout: function() { alert(“Saving form data.“); document.forms[0].draft.disabled = 'disabled'; } } );
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Durchgehendes Tippen
Problem
Regelmäßiges Eingeben in ein Textfeld
Das Formular wird nie automatisch abgeschickt
Ansatz
Eingaben zählen (Maus, Tastatur)
Ab gewisser Anzahl an Änderungen das Speichern erzwingen
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse AutoSave: HitsCounter
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse AutoSave mit HitsCounter
Neuer Parameter hitsLimitSpezifiziert die maximaleAnzahl ungespeicherterÄnderungen
Observer-Events: • update: Element hat sich geändert• timeout: Timeout wurde erreicht• hitslimit: Maximale Anz. an Änderungen erreicht
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Klasse Note
Konkrete Anwendung von AutoSave.js
Verwendet AutoSave
Observer-Events
update: aktivieren
timeout: Daten senden, deaktivieren
hitslimit: Daten senden, deaktivieren
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Speicherung der Daten (1)
Implementierung des AJAX-Requests
Abhängig vom eingesetzten AJAX-Framework
Datenbank-Schema zum Ablegen der AutoSave-Daten
Abhängig von der Anwendung
Extra-Tabelle „autosaves“
Inmitten der Nutzdaten: flag: „draft“
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Speicherung der Daten (2)
Fall: Speicherung der Daten inmitten der Nutzdaten
1. AutoSave-Request würde einen neuen Eintrag anlegen
INSERT INTO …
Folge-Requests müssen diesen Datensatz aktualisieren
UPDATE … WHERE ID = …
Deshalb
AutoSave-Request muss den
PK zurückgeben
Notizen
id INT(10) PRIMARY KEY
title VARCHAR(50)
body TEXT
lang VARCHAR(5)
draft TINYINT(1)
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Speicherung der Daten (2)
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Formularvalidierung
Beim Empfangen von Formularen
Formularvalidierung
Sind alle Felder ausgefüllt
Überschreiten die Felder nicht die Maximallänge
Bei AutoSave-Requests
Keine oder nur einfache Validierung!
Sonst: Keine AutoSave der Eingaben obwohl evtl nur ein Feld fehlt
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
DEMONSTRATION ERGEBNIS
• Framework für AutoSaving
• Kann universell eingesetzt werden
• AJAX-Requests und Persistence müssen selbst implementiert werden
AutoSave für AJAX-Formulare
AJAX IN ACTION 2007 - Timo Holzherr
Verweise
[1] Prototype
http://www.prototypejs.org/
JS-Bibliothek für einfache DOM-Manipulation und AJAX-Requests
[2] Folien und Beispiele dieses Vortrags
http://timo.holzherr.de/aia2007