Das man mit JavaScript und AJAX nicht nur tolle, sondern auch wirklich nützliche Funktionalität in einer Webanwendung unterbringen kann, ist nicht von der Hand zu weisen. Eingabefelder, die schon beim Eintippen mögliche Treffer anzeigen, sind nur ein Beispiel und auf fast allen bekannten Webseiten zu finden.
Solche Funktionalität lässt sich auch in bestehenden Anwendungen nachrüsten, ohne das komplette Projekt umzukrempeln. Die folgenden Beispiele stammen aus der Erweiterung einer Struts 1 Anwendung, in die wir nachträglich Ext JS (Version 2.2) integriert hatten, um einige der Widgets von Ext JS zu nutzen.
1. Ext JS zum Projekt hinzufügen
2. Einbinden von Ext JS in die JSP’s
3. AJAX Request mit Ext JS (Beispiel)
4. Response (HTML oder JSON)
Anmerkung: Expression Language (EL) muss vom Server unterstützt werden.
1. Ext JS zum Projekt hinzufügen
Das Archiv, das man von der Ext JS Seite herunterladen kann, enthält neben den eigentlichen Bibliotheken auch zahlreiche Beispiele und Sourcen, die von der Anwendung nicht benötigt werden. Folgende Dateien* und Verzeichnisse* kopiert man in sein Projekt z.B. in das Unterverzeichnis WebContent/js/ext:
- ext-all.js
- ext-core.js
- /adapter
- /build
- /resources
* Ext JS Version 2.2 oder 2.3 ohne Debug
Man kann auch die oben erwähnten Dateien und Verzeichnisse in ein ZIP packen, z.B. ext-2.2.zip und dieses beim Build der Anwendung durch Ant enstspr. auspacken und zum WAR hinzufügen lassen. Damit ist einigermaßen sichergestellt, dass die Ext-Dateien nicht editiert werden und zudem hat man nur eine Datei auf dem SVN bzw. CVS liegen (erleichtert auch den Wechsel auf eine andere Ext-Version). Das ist aber absolut optional.
2. Einbinden von Ext JS in die JSP’s
Um Ext verfügbar zu machen und den Standardadapter zu nutzen sind folgende Zeilen im HTML-Head nötig:
<script type="text/javascript"
src="${pageContext.request.contextPath}/js/ext/ext-all.js"></script>
<script type="text/javascript"
src="${pageContext.request.contextPath}/js/ext/adapter/ext/ext-base.js"></script> |
Will man einige der Widgets von Ext nutzen, ist zusätzlich das CSS von Ext einzubinden:
<link rel="stylesheet" type="text/css"
href="${pageContext.request.contextPath}/js/ext/resources/css/ext-all.css" /> |
und ggf. ein Theme auszuwählen, wenn man nicht das Standard-Blau verwenden möchte:
<link rel="stylesheet" type="text/css" id="theme"
href="${pageContext.request.contextPath}/js/ext/resources/css/xtheme-gray.css" /> |
Eine Besonderheit: Ich weiß nicht, ob die Ext Entwickler das mit Absicht gemacht haben und welchen Zweck es haben soll, aber Ext “funkt” nach Hause. An einigen Stellen wird ein 1×1 Pixel Image von der Ext-Seite abgerufen. Will man das umgehen und stattdessen das Image lokal laden, sollte man diese Zeilen im Header unterbringen:
<script type="text/javascript">
// url for empty image otherwise http://extjs.com will be called
Ext.BLANK_IMAGE_URL = '${pageContext.request.contextPath}/js/ext/resources/images/default/s.gif';
</script> |
3. AJAX Request mit Ext JS (Beispiel)
Nun werden wir einen Ajax Request ausführen um z.B. den Wert eines Formularfelds an den Server zu übermitteln, ohne die komplette Seite neu laden zu müssen (also kein Form.submit()).
Die Funktion würde wie folgt aussehen:
function sendValue(value) {
Ext.Ajax.request({
url: '${pageContext.request.contextPath}/sendValue.do',
params: { newValue: value },
success: function(result, response) {
// in diesem Fall soll nichts gemacht werden
},
failure: function(result, request) {
Ext.Msg.alert('Fehler', 'Request konnte nicht ausgeführt werden');
}
});
} |
Serverseitig rufen wir hiermit eine entspr. gemappte StrutsAction auf.
Auszug aus der struts-config.xml:
<action path="/sendValue"
type="de.baderundjene.example.ValueChangeAction">
</action> |
Ein Form kann man sich sogar sparen und den Parameter direkt auslesen:
public final class ValueChangeAction extends Action {
@Override
public final ActionForward execute(....) {
final String newValue = request.getParameter("newValue");
// irgendwas mit dem newValue machen
return null;
}
} |
Am Ende geben wir null zurück, es soll ja kein ActionForward erfolgen und der Response soll leer sein.
4. Response (HTML oder JSON)
Möchten wir aber einen Wert, Werte oder ein HTML-Fragment zurückgeben, muss dieses als String in den Response geschrieben werden.
Um Daten an den Client (Browser) zu schicken, die dort per JavaScript verarbeitet werden sollen um z.B. die Werte einer Select-Box zu verändern, eignet sich JSON (JavaScript Object Notation) besonders gut. Details zu JSON findet man ganz leicht im Internet. Bevor wir also in unserer Action null zurückgeben, schreiben wir unseren JSON-String in den Response:
// ein Attribut data mit dem Wert Test
final String json = "({data:'Test'})";
// HttpServletResponse response;
// hier können wir auch das Encoding auf z.B. UTF-8 stellen
response.setContentType("text/json; charset=UTF-8");
final PrintWriter out = response.getWriter();
out.println(json);
out.flush(); |
Im JavaScript erweitern wir unsere success Methode:
function sendValue(value) {
Ext.Ajax.request({
url: '${pageContext.request.contextPath}/sendValue.do',
params: { newValue: value },
success: function(result, response) {
var responseObj = Ext.decode(result.responseText);
Ext.Msg.alert('Erfolg', responseObj.data);
},
failure: function(result, request) {
Ext.Msg.alert('Fehler', 'Request konnte nicht ausgeführt werden');
}
});
} |
Möchte man ein HTML-Fragment zurückgeben, um z.B. einen Teil der Seite neu zu laden, kann man in der Action einen ActionForward auf eine entspr. JSP durchführen. Diese JSP enthält dann eben keine ganze HTML-Seite sondern nur den Teil, den wir neu laden möchten, z.B. eine Tabelle mit den Kundendaten.
D.h. struts-config.xml anpassen:
<action path="/sendValue"
type="de.baderundjene.example.ValueChangeAction">
<forward name="success" path="inc/customerTable.jsp"/>
</action> |
und statt null den ActionForward mapping.findForward(“success”); zurückgeben.
Unsere angepasste JavaScript Methode:
function sendValue(value) {
Ext.Ajax.request({
url: '${pageContext.request.contextPath}/sendValue.do',
params: { newValue: value },
success: function(result, response) {
// <div id="customer-data"> </div>
var customerDiv = Ext.get('customer-data');
customerDiv.update(response.responseText, true);
},
failure: function(result, request) {
Ext.Msg.alert('Fehler', 'Request konnte nicht ausgeführt werden');
}
});
} |
Fazit
Ein Projekt kann sehr leicht mit Ajax erweitert werden ohne bestehende Funktionalität anzufassen. Welches MVC-Framework hier zum Einsatz kommt, spielt eigentlich keine Rolle. Modernere Frameworks bieten allerdings teilweise out-of-the-box Unterstützung für Ajax und JSON an.
Bei unserem Projekt haben wir neue Erweiterungen direkt mit Ext JS implementiert, möglichst alle eigenen oder sonstiges JavaScript durch Ext Funktionen ersetzt und bauen jetzt bei Bedarf und wenn es sinnvoll ist, ältere Teile der Anwendung auf Ext um. Z.B. verwenden wir das Tree Widget inkl. Drag and Drop und Kontextmenü, welches sehr mächtig ist und einen sehr stabilen Eindruck macht.
Zu wünschen wäre jetzt nur noch eine bessere Unterstützung durch eclipse, aber das kommt vielleicht auch noch.
Falls es sich ergibt, werde ich auch noch näher auf interessante Widgets von Ext JS eingehen.
Viel Spaß und Erfolg beim Ausprobieren und Anwenden.
Christof Aenderl