Mit diesem Beitrag setze ich meine kleine Serie zu lang laufenden PHP-“RUN”s und asynchron gestarteten “CHECKER”-Programmen für Ajax-basierte Statusüberprüfungen des “RUN”s fort. Siehe:
Fallen beim Statuscheck lang laufender PHP-Jobs mit Ajax – I
Fallen beim Statuscheck lang laufender PHP-Jobs mit Ajax – II
Wir hatten bereits diskutiert, dass beide Jobs von ein und derselben Webseite über Ajax-Transaktionen zu starten und zu managen sind. Im besonderen ist das Status-Polling durch periodisches Starten eines “CHECKER”-Jobs zu erledigen. Das Starten erfolgt über eine periodische, jeweils nur kurze Zeit dauernde Ajax-Interaktion mit dem Server.
In beiden Fällen – beim einmaligen Start des RUNs und beim periodischen Start des CHECKERS – sind i.d.R. einige Daten zum Server zu übertragen. Diese Daten kann man z.B. in unsichtbaren Feldern eines Formulars hinterlegen und vor dem Ajax-Transfer serialisieren.
Im letzten Beitrag hatte ich bereits angedeutet, dass es im Zusammenhang mit der klaren Strukturierung der parallelen Ajax-Handhabung von mehreren Formularen folgende Vorgehensweisen sinnvoll sind:
- Jedes HTML Formular für einen per Ajax zu startenden Job wird auf der Javascript Seite vollständig auf ein zugehöriges, spezifisches Kontroll-Objekt “K” abgebildet. Definierte Methoden dieses Kontroll-Objektes übernehmen nicht nur die Serialisierung der asynchron zum Server zu transferierenden Daten, sondern kümmern sich auch um einen angemessene Reaktion auf Ajax-Rückmeldungen des Servers und übergeben bei Bedarf Teile der Server-Antwort an bestimmte HTML-Objekte (bzw. weitere zugehörige, abstrakte JS-Kontroll-Objekte).
- Um Submit-Events durch “K”-Methoden zu kontrollieren, ist die $.proxy – Funktion einzusetzen. Sie sorgt dafür, dass der “this”-Operator bei der Ersetzung der Submit-Funktionalität des HTML-Formulars (und seines Submit-Buttons) durch eine spezielle Submit-Methode (z.B. submit_event() ) des Kontroll-Objektes innerhalb dieser Methode weiterhin auf das Kontroll-Objekt zeigt – anstatt dem jQuery-Standard entsprechend auf das eventauslösende HTML-Element (also das Formular bzw. den dortigen Submit-Button).
Wir wollen diesen Punkt nachfolgend noch etwas vertiefen und anschließend beleuchten, wie sich der “this” Operator unter jQuery in den Methoden des Kontroll-Objektes zum Formular verhält. Wir berühren dabei zwar nur elementares jQuery/Ajax-Know-How, aber vielleicht ist das ja für den einen oder anderen interessant. Und es liefert die Basis für die Realisierung unseres RUN/CHECKER-Szenarios.
Einfacher Beispielcode für die Verwendung von $.proxy(), Ajax und this in einem Kontroll-Objekt zu einem Formular
Wir betrachten also ein Formular “F”, für das wir ein zugehöriges Kontroll-Objekt “K” definiert haben. Der Submit-Event des Formulars soll über eine Callback-Funktion “submit_event()” des “K”-Objektes abgewickelt und kontrolliert werden. Die spezielle Submit-Methode des Kontroll-Objekts ist über eine entsprechende “prototype”-Definition festzulegen. Also z.b. etwa so:
.... .... K = new Ctrl_k(); // in diesem Beispiel nur der Einfachheit halber eine globale Definition function Ctrl_K () { // Define and manage handles to other JS Control objects ...... // Define and manage jQuery selectors for the form and its elements ..... ....... this.id_status_form = "# ...."; ....... ....... // Register Events - e.g. replace the submit event of the form by a local method of the "K" object $(this.id_status_form).submit( $.proxy(this, 'submit_event') ); } Ctrl_K.prototype.submit_event = function(e) { e.preventDefault(); .... .... // Prepare Ajax transaction var url = $(this.id_status_form).attr('action'); var form_data = $(this.id_status_form).serialize(); var return_data_type = 'json'; ...... $.ajaxSetup({ contentType: "application/x-www-form-urlencoded; charset=ISO-8859-1", context: this, error: this.status_error, ....... }); ..... ..... // Perform an Ajax transaction $.post(url, form_data, this.status_response, return_data_type); ....... ....... }; Ctrl_K.prototype.status_response = function(status_result) { // Do something with the Ajax (Json) response ..... this.msg = status_result.msg; ..... }; Ctrl_K.prototype.status_error = function(jqxhr, error_type) { // Analyze the error ..... ..... };
“this” in der per $.proxy() adressierten Methode “submit_event” bezieht sich in diesem Beispiel auf das K-Objekt selber und nicht, wie es ansonsten unter jQuery der Fall wäre, auf das HTML-Objekt, das den Submit-Event ausgelöst hat (z.B. der Submit-Button des Formulars). Nur wegen $.proxy() können wir “this” in der gewählten Form überhaupt erfolgreich und in seiner angestammten Bedeutung in “submit_event()” verwenden.
Das bedeutet: Durch den konsequenten Einsatz von $.proxy ergibt sich die Möglichkeit innerhalb des JS-Codes systematisch und sehr weitgehend von Details der HTML-Oberfläche zu abstrahieren, ohne die intuitive Bedeutung für Objekt-Methoden unter JS/jQuery zu verletzen. Jeder HTML-Bereich (u.a. ein Formularbereich), der mit JS/jQuery zu behandeln ist, kann in Form eines
- zugehörigen Kontroll-Objektes und
- zugehöriger registrierter Methoden für die Events seiner HTML-Objekte
behandelt werden. Die notwendigen Kontroll-Objekte werden i.d.R. beim Laden der Web-Seite über Initialisierungsfunktionen erzeugt. Das ermöglicht einen sehr klaren Code-Aufbau – auch dann wenn man wie im RUN/CHECKER-Szenario für ein und dieselbe Webpage gleich mehrere Formulare und Ergebnisbereiche parallel und periodisch handhaben muss.
“this” und der Kontext der Ajax Success bzw. Error-Funktion
Wir wenden uns nun einem weiteren interessanten Punkt zu. Worauf verweist eigentlich “this” in der Ajax-Callback-Funktion “status_resonse” des “K”-Objektes ? Das ist ja die Funktion, die im Falle des Erfolges der Ajax-Transaktion aufgerufen werden soll.
In unserem Beispiel oben ist die Ajax Success-Funktion als Parameter der jQuery Convenience-Funktion $.post(..)” festgelegt worden. Hätte man nur $.ajax({ … }) zum Auslösen der Ajax-Transaktion verwendet, hätte man dort eine Callback-Funktion über
$.ajax({ …, success: this.status_response, … })
festgelegt. Etwas Analoges gilt natürlich für die Callback-Funktion zur Ajax-Fehlerbehandlung.
So ohne weiteres ist es leider überhaupt nicht klar, was “this” in einer Ajax-Success/Error-Callback-Funktion unter jQuery bedeutet oder bedeuten soll. Die Doku (http://api.jquery.com/jQuery.ajax/) sagt dazu:
By default, the context is an object that represents the ajax settings used in the call ($.ajaxSettings merged with the settings passed to $.ajax).
Na, wunderbar. Damit bezöge sich das “this” in unserer schönen Methode des “K”-Objekts also leider gar nicht auf das “K”-Objekt. Das diese Feststellung zutrifft, sollte der Leser selber testen. Siehe aber die Links unten.
Das
wollen wir aber nicht. Wenn wir eines klareren Programm-Designs halber schon ein abstraktes Kontroll-Objekt einführen, soll das “this” in seinen Methoden bitte schön auch immer auf das Kontroll-Objekt selbst verweisen – Ajax hin oder her. jQuery ist das aber zu Recht egal:
Bei etwas Nachdenken wird einem klar, dass der Kontext, auf den sich “this” im Ajax Success-Callbakc beziehen soll, sich auch nicht wirklich standardmäßig zuweisen lassen kann. Denn man hätte die Callback-Funktion ja auch direkt deklarieren können – nach dem Muster:
$.ajaxSetup({ ..., success: function () { this.xxx = , ... }, ..... ..... })
Was soll “this” denn dann sein ??? Das hängt wohl von Bedarf und Design der Applikation ab. Die Antwort ist also:
Der Entwickler muss den Context für die Callback-Funktion explizit setzen. Er ergibt sich in sinnvoller Weise nicht von allein. Dazu verwendet man das Attribut “context” in demjenigen Objekt, das wir bei lokalen Ajax-Transaktionen der Funktion $.ajax() oder bei einer globalen Festlegung für alle Ajax -Transaktionen der Funktion $.ajaxSetup() übergeben.
Viele Entwickler weisen nun den Context des Success-Callbacks meist einem (hidden) HTML-Objekt zu. In unserem Ansatz mit einem abstrakten Kontroll-Objekt zum Formular tun wir das natürlich nicht! Wir weisen den Kontext vielmehr dem erzeugten Kontroll-Objekt “K” selbst zu. Sollte Output in anderen, von ihm nicht kontrollierten HTML-Objekte erzeugt werden müssen, können die notwendigen Daten aus der Ajax-Antwort ja immer noch in definierter Weise an andere Kontroll-Objekte weitergereicht werden.
Wenn das Objekt “K” global definiert ist, kann man also schreiben:
$.ajaxSetup({ ..., context: K, ..... })
Aufgrund des Einsatzes von $.proxy() geht es aber deutlich eleganter:
$.ajaxSetup({ ..., context: this, ..... })
So, nun haben wir gesehen, wie wir Formulare und zugehörige Ajax-Transaktionen mit $.proxy() und spezifischen Methoden von Kontroll-Objekten handhaben können. Im nächsten Beitrag dieser Serie
Fallen beim Statuscheck lang laufender PHP-Jobs mit Ajax – IV
befasse ich mich mit dem Timing des periodischen Aufrufs des CHECKER-Prozesses.
Links
http://stackoverflow.com/questions/3863536/how-to-pass-context-in-jquery-ajax-success-callback-function
http://stackoverflow.com/questions/6394812/this-inside-of-ajax-success-not-working
http://stackoverflow.com/questions/11027276/jquery-this-not-accessible-after-ajax-post