Perl-Webanwendungen mit Ajax: Ajax bringt Glanz auf Perlen
Seit ein paar altbekannte Technologien zusammengefasst und unter dem Schlagwort „Ajax“ verbreitet werden, ist ein wahrer Hype entstanden. Spätestens mit Google Maps ist der Durchbruch vollends gelungen. Kaum eine große Website, egal ob Flickr oder Xing, verzichtet auf die Möglichkeit, Daten vom Server nachzuladen, ohne die komplette Seite neu laden zu müssen.
Viele dynamische Websites werden mit Perl realisiert – vom einfachen Formmailer bis hin zu del.icio.us oder imdb.com. Auch auf diesen Seiten kommen Ajax-Technologien zum Einsatz. Auf CPAN [1] finden Perl-Programmierer Unterstützung für die Implementierung von Ajax-Funktionen.
CGI::Ajax
Das Modul CGI::Ajax übernimmt wichtige Aufgaben: Es sorgt dafür, dass JavaScript-Funktionen auf Funktionen im Perl-Skript gemappt werden.
my $ajax = CGI::Ajax->new( JSFunktion => \&perl_function);
Listing 1
Der Programmierer muss dazu keine Zeile JavaScript schreiben und sich auch nicht um die Ausgabe des HTML-Codes kümmern. Die HTML-Ausgabe wird wie gewohnt generiert und dann einem Objekt von CGI::Ajax übergeben.
Nur für den Auslöser der JS-Funktionen muss ein JavaScript erstellt werden. Im Template sollte ein Link etwa so aussehen:
<a xhref="..." onclick="JSFunktion(['action__delete_task','id__1'],['task1']);">...</a>
Listing 2
Ein Klick auf diesen Link ruft die JavaScript-Funktion „JSFunktion“ auf. Der Funktion wird eine Liste mit Parametern übergeben, die als Parameter des GET-Requests an den Server weitergegeben werden. Das „__“ trennt den Parameter-Namen von seinem Wert. In diesem Fall wird zum Beispiel „action=delete_task&id=1“ an den Server übermittelt. So können beliebige Parameter übergeben werden. Die zweite Liste enthält die IDs von Elementen, die durch die Antwort des Servers modifiziert werden sollen. In diesem Fall gibt es ein <div> mit dem Namen „task1“, das die Antwort des Servers enthalten soll.
Einige Dinge gilt es zu beachten: Eingebaute JavaScript-Funktionen dürfen beispielsweise nicht verwendet werden. Das ist auch der Grund, warum im Beispiel die Funktion „delete_task“ anstatt „delete“ heißt. Darüber hinaus sollte man sich unter Windows nicht wundern, wenn in den URLs, die für den Request benutzt werden, bei längeren Skriptnamen so etwas wie „INDEX_~1.CGI“ als URL in der JavaScript-Funktion steht. Das liegt daran, dass Windows intern die Länge von Dateinamen auf 8 Zeichen kürzt.
Leicht irreführend ist auch die Meldung eines „Internal Server Error“ – in den Log-Dateien findet sich eine Meldung „POSSIBLE SECURITY INCIDENT!“ – nach dem Aufruf einer nicht-existenten Funktion des CGI-Skript durch den GET-Request des XMLHttpRequest-Objekts.
Auch bei der Verwendung von CGI::Application muss man aufpassen. Da die Perl-Subroutinen direkt ausgeführt werden, steht das Objekt von CGI::Application nicht zur Verfügung. Deshalb muss man auf Codereferenzen verzichten und stattdessen eine „externe“ URL angeben.
#!/usr/bin/perl use CGI; use CGI::Ajax; my $cgi = CGI->new(); my $ajax = CGI::Ajax->new(jsecho => \&perlecho); print $ajax->build_html( $cgi, html() ); sub perlecho{ return @_; } sub html{ return q~<html><body> <input type="text" id="echofield" onkeyup="jsecho(['echofield'],['test'])"> <div id="test"></div> </body></html> ~; }
Listing 3
Was geschieht hier? In den ersten drei Zeilen werden die benötigten Module geladen. Dann wird das Mapping realisiert: CGI::Ajax erzeugt eine JavaScript-Funktion „jsecho“, in der mit einem XMLHttpRequest-Objekt ein GET-Request aufgerufen wird. Die Funktion, die im CGI-Skript ausgeführt werden soll, heißt „perlecho“. Zeile sechs löst die Header und die HTML-Ausgabe aus. build_html erzeugt den JavaScript-Code, der in den Head-Bereich des HTML-Codes eingefügt wird. Im Input-Feld wird festgelegt, dass auf Tastendruck die Funktion „jsecho“ aufgerufen werden soll. Dabei wird der Wert des HTML-Elements mit der ID ‚echofield‘ an den Server übergeben. Das Ergebnis wird dann im HTML-Element mit der ID ‚test‘ dargestellt.
Das Modul ist sehr gut für relativ einfache Ajax-Anwendungen geeignet. Sobald es in andere Frameworks integriert werden soll, ist der „Spieltrieb“ des Entwicklers gefordert. Oft sind Lösungen im Internet zu finden, da andere sich schon mit den selben Problemen auseinandersetzen mussten.
HTML::Prototype
Prototype [2] ist ein sehr bekanntes Framework für Ajax. Auch hierfür gibt es auf CPAN ein entsprechendes Modul, das den Programmierer bei seiner Arbeit unterstützt. Mit HTML::Prototype kann das Schreiben von JavaScript komplett vermieden werden, was für JavaScript-Einsteiger sicherlich von Vorteil ist. Mit den folgenden Codezeilen wird die Prototype-Bibliothek ausgegeben:
print $prototype->define_javascript_functions;
Listing 4
Die meisten Methoden von HTML::Prototype liefern den JavaScript-Bereich für die Ajax-Funktionalität, während die HTML-Elemente im Template definiert werden müssen. Es gibt allerdings einige Ausnahmen: Funktionen, die einen JavaScript-Event-Handler verwenden (zum Beispiel link_to_remote), liefern das komplette HTML-Element mit dem zugehörigen JavaScript.
#!/usr/bin/perl -w use CGI; use HTML::Prototype; my $cgi = CGI->new(); print $cgi->header(); my %params = $cgi->Vars; if( $params{action} ){ print $params{test}; } else{ my $field = $pt->observe_field('testid',{ update => 'view', url => 'html_prototype_test.cgi', with => "'action=echo;test='+value", frequency => 1, }); my $proto = $pt->define_javascript_functions; print qq~<html><head>$proto</head> <body><input type="text" id="testid"> <div id="view"></div> $field</body></html>~; }
Listing 5
Zunächst werden ein neues CGI-Objekt erzeugt, der Header ausgelesen und die Parameter geparst. Gibt es einen ‚action‘-Parameter, wird der Wert des Parameters ‚test‘ zurückgeliefert.
Nun folgt die Aktion, welche die Ajax-Funktionalität abfängt und auf der Seite darstellt. Dies geschieht über ein so genanntes „observe_field“ – ein Textfeld, das in gewissen Abständen (über „frequency“ einstellbar) die mit dem Parameter „url“ festgelegte URL aufruft. Der erste Parameter für das observe_field ist die ID des Textfeldes, dessen Inhalt überwacht werden soll. Was an die URL übergeben werden soll, wird über den Parameter „with“ festgelegt,
Die Prototype-Bibliothek wird in der Variable „$proto“ gespeichert. Für die HTML-Ausgabe wird das gesamte JavaScript in den head-Bereich des HTML geschrieben und die JavaScript-Funktion, die das Textfeld überwacht, in den <div>-Container unterhalb des Feldes eingesetzt.
Genauso übersichtlich und einfach sind alle weiteren Funktionen des Moduls nutzbar. Mittlerweile ist ein Großteil der Prototype-Funktionen im Modul umgesetzt.
Fallen von HTML::Prototype
Wenn Parameter an eine URL übergeben werden sollen (zum Beispiel http://localhost/script.cgi?action=test), muss bei den Optionen der Parameter „with“ eingestellt werden. Dabei ist darauf zu achten, dass der String, der im JavaScript ankommt, mit Quotes versehen ist. Deshalb sieht es zum Beispiel so aus:
with => "'action=show_detail;id=$id'",
Listing 6
Mit diesem Modul kann man die Arbeit mit JavaScript auf ein absolutes Minimum reduzieren. Allerdings ist die Dokumentation an einigen Stellen sehr dürftig. Gerade für Anfänger ist es schwierig, alle Funktionalitäten zu nutzen – da hilft nur ausprobieren.
Fazit
Mit CGI::Ajax gibt es ein Modul, das für Ajax-Einsteiger sehr gut geeignet ist. Die Einbindung in Frameworks ist nicht optimal, aber mit ein paar kleinen Tricks gut machbar. Wer sich mit Ajax und der Prototype-Bibliothek auskennt, der wird sich mit HTML::Prototype anfreunden können. Die dürftige Dokumentation wird durch die umfangreiche Funktionalität wieder wettgemacht.