Fallen beim Statuscheck lang laufender PHP-Jobs mit Ajax – III

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:

  1. 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).
  2. 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