Ich sitze nun schon seit einiger Zeit an meinem Client und bin dabei, die Funktionalität soweit zu verwirklichen, dass ein einfaches Arbeiten (CRUD), mit jeweils User, Task und Group möglich ist. Ich habe bemerkt, dass die Oberfläche die wenigsten Probleme macht, was eine Herausforderung ist, sind die vielen nebenläufigen Request-Abfragen.
Ich habe (hatte) mich dazu entschlossen (hier mal anhand eines Users), jeweils per JS ein User Objekt (welches von den Attributen her meinem JPA Objekt entspricht), sowie ein UserService Objekt, welches den Zugriff (Ändern/Laden/Speichern/Cachen) meiner User-objekte.
Liegt ein Objekt nicht im lokalen Cache (ein Array aus User-objekten) vor, so soll es Remote vom Server geholt werden.
Das klappt auch soweit alles. Leider handelt es sich bei dem Request (AJAX-typisch) um einen asynchronen Request,so dass die Daten im Hintergrund geladen werden, während die Verarbeitung im Vordergrund weiter läuft. Es passiert dann des öfteren, dass auf Daten zugegriffen werden soll, die noch nicht vollständig geladen sind. Dies passiert zum Beispiel auch, wenn innerhalb eines Task-Objektes ein User-Objekt abgefragt wird, welches noch nicht lokal im Cache vorhanden ist. Nach einiger Literatur (u.a. auch auf den Developer Seite bei Yahoo) und ein wenig Austausch mit Willem (vielen Danke noch mal hierfür) bin ich nun zu dem Ergebnis gekommen, dass ich meine Architektur so abändere, sodass diese stärker mit Callbacks arbeiten.
YUI sieht hierbei ein Callback-Objekt vor, welches jeweils eine Methode bei Erfolg und eine Methode bei Misserfolg zur Verfügung stellt.
Ein großes Problem ist hierbei aber auch die Übergabe der Referenzen.
Da zum Beispiel meine UserService Klasse instanziert wird und das Cache Array dann ein Attribut des Objektes ist.
So ist es dann notwendig, dass einer Callback-Klasse diese Referenz ebenfalls zur Verfügung gestellt wird.
Dies kann man u.a. durch ein "Zwischen referenzieren des this-pointers machen:
function failureHandler(o){
var out = "COTODO.UserService.getRemote: id="+id;
//Referenz steht normalerweise im UserAttribute
var rUrl = "resources/users/"+id+"/";
var self = this;
YAHOO.log("Failure handler called; http status: " + o.status, "warn", "User.js");
}
YAHOO.util.Connect.asyncRequest('GET', sUrl, { success:callback, failure:failureHandler });
};
Im Moment bin ich dabei so Dinge wie:
//US = UserService Instanz
return US.get(this.created_by);
}
if(!this.exists(id)){
return this.getRemote(id);
}
COTODO.debug(this.list[id],"get");
return this.list[id];
};
Eine weitere interessante Möglichkeit wäre die Nutzung von XML-Inseln. Also die Möglichkeit, dass jedes Objekt (und in diesem Objekt dann jeweils eine Callback-Methode) dafür verantwortlich ist, dass seine eigene Daten in den DOM Tree der Seite gesetzt werden.
D.h. jedes User-Objekt würde sicher selber - bei Bedarf - als "li" - node in ein "ul" Element auf der Seite setzen und so auch tatsächlich in der GUI die Userliste füllen. Alle User Attribute könnte man entweder als li Attribute setzen (den Tag in dieser Hinsicht "verschmutzen") oder aber halt in einen versteckten Layer packen. Sicherlich eine interessante Idee, aber letztendlich halt nicht wirklich 100% MVC, aber durchaus gängige Praxis im AJAX Umfeld.
Mein UserService würde dann auch kein internes Cache Array haben - sondern direkt auf den DOM-Knoten zugreifen.
Um den Zugriff zu vereinfachen habe ich mir schon ein paar Ersatz-Methoden geschrieben, um die Dinge zu verkürzen. Wer Prototype kennt, wird sich zuhause fühlen.
var num = num || 0;
try{
return this.$E(id)[num].firstChild.nodeValue;
}catch(error){
return null;
}
};
Object.prototype.$E = function(id){
return this.getElementsByTagName(id);
};
Letztendlich werden hier aber noch alle JS-Objekte verändert, sodass auch ein Array besagte Methoden enthält.
Ich werde mal schauen ob sich das irgendwie noch verfeinern lässt.
Blogged with Flock