Domino 9 und frühere Versionen > ND6: Entwicklung

Domino und Prototype/Scriptaculous

<< < (2/2)

flaite:
Hab bei der Entwicklung des serverseitigen Teils gemerkt, dass ich 2 Parameter beim Client vergessen habe. Einer ist sehr wichtig für Model View Controller (MVC).


--- Code: ---                       "AjaxServlet",
                    {
                        method: 'get',
                        parameters: "action=" + action + " unid=" + unid + " cat=" + newCat,
                        onComplete: parseTransferin,
                        onFailure: showAjaxError
                    }

--- Ende Code ---

Vor allem das action= ist wichtig. Das MVC soll bewirken, dass ALLE Ajax Requests gegen eine serverseitige Anwendung von ein und demselben Agenten verarbeitet werden. Sonst müllt man die Anwendung mit Tonnen von kleinen Agenten voll.

Gut. Was passiert jetzt auf der Serverseite.
1. Die Serverseite nimmt eine Anfrage aus dem JavaScript entgegen.
2. verarbeitet diese
3. Schickt Daten im JSON Format zurück. XML ginge auch, aber JSON ist von JavaScript einfacher zu verarbeiten.

Wie verarbeitet der Notes Agent die Anfrage?
Das folgende hat übrigens noch kein fertiges Fehlerhandling.
Die Parameter werden als CGI Variablen an die URL des Agenten gehängt. Darum kümmert sich Prototype automatisch, wenn man parameters: wie oben schreibt.
Ein Agent sollte das so auslesen können:

--- Code: ---Set docContext = s.DocumentContext
queryStr = docContext.getItemValue("QUERY_STRING_DECODED")

--- Ende Code ---
ist halt ne CGI Variable.

In queryStr sollte dann sowas stehen wie:

--- Code: ---"?openAgent&action=changeCategory&cat=blueChips&unid_doc_cur=9093458034853045"

--- Ende Code ---
Die einzelnen name=wert Paare zwischen den & lassen sich durch diese einfache Funktion in eine leicht zu handhabende List-Struktur bringen:

--- Code: ---Function webParamsAsList (inVal As String) As Variant
Dim firstAmper As Integer
Dim ret List As String

Dim arrNameValuePairs As Variant
Dim elemNameValuePair As String
Dim arrNameValuePair As Variant
Dim i As Integer


firstAmper = Instr(inVal, "&")

inVal = Right$(inVal, Len(inVal) - firstAmper)

arrNameValuePairs = Split(inVal, "&")
For i = 0 To Ubound(arrNameValuePairs)
elemNameValuePair = arrNameValuePairs(i)
arrNameValuePair = Split(arrNameValuePairs(i), "=")
ret(Lcase(arrNameValuePair(0))) = Lcase(arrNameValuePair(1))

Next




webParamsAsList = ret



End Function

--- Ende Code ---

Nun kommt der Kontroller. Da wird einfach nur der action-Parameter ausgelesen und in die entsprechende Funktion zur Weiterverarbeitung aufgerufen.

--- Code: ---webParams = webParamsAsList(inVal)
resp = ""
If Lcase(webParams("action")) = "changecategory" Then
resp = processChangeCategory(s, webParams)

End If

--- Ende Code ---

Am Ende soll dann nur noch etwas an den Browser zurückgeliefert werden. Kommt später.

flaite:
Und das JSON Zeugs geht auf der Serverseite (also im lotusScript Agenten) so:

Man erzeugt aus den Variablen ein bischen JSON formatiertes Zeugs.


--- Code: ---processChangeCategory = "{" & strNL &_
|"transferout":| & |"| & strTransferout & |",| & strNL &_
|"unid":| & |"| &  strUnid & |"| & strNL &_
"}"

--- Ende Code ---

Und das gibt man dann über print aus. Wichtig ist, dass vorher noch das Encoding des über http zurückgesendeten Streams auf text/javascript stellt und wohl auch das encoding auf dem in der Webseite (wo das ajax clients Zeugs drin ist) übereinstimmt.
Für Encoding gibts für LotusScript im Gegensatz zu jeder mir bekannten Webprorammierumgebung keine Methode. Man muß das als erstes Print Statement mit 1 Leerzeile dadrunter ausgeben.


--- Code: ---Print |Content-Type:text/javascript;charset=iso-8859-1| & Chr$(13) & Chr$(10) & Chr$(13) & Chr$(10)
Print respBody

--- Ende Code ---
(respBody entspricht der obigen Variable processChangeCategory)

Ich hatte ein bischen zu kämpfen mit dem Encoding. Er schien Probleme zu haben, als ich das erst auf UTF-8 stehen hatte. Domino gibt zwar standardmässig UTF-8 aus, in der Webseite stand aber ISO-8859-1. Kann das aber jetzt nicht mehr reproduzieren. Leicht mysteriös.

JSON läßt sich in JavaScript viel einfacher verarbeiten als xml. Das ist einfach nur ein eval statement und ich hab das Objekt.


--- Code: ---function parseTransferin(transport) {
                    var response = transport.responseText;
                    var jsonObj = eval("(" + response + ")");
                   
                    alert ("jsonObj.uind=" + jsonObj.unid)
                    alert("jsonObj.transferout=" + jsonObj.transferout)
                   
                    //ui.transferin.innerHTML = jsonObj.transferout;
                    //processTransferin();

                 }

--- Ende Code ---
Fazit soweit:
Die Prototype Bibliothek bringt Vorteile, die ich noch gar nicht alle ausereizt habe.
JSON ist voll in Ordnung und sollte XML vorgezogen werden.
Ajax sind eigentlich so eine Art Webservice zwischen einer JavaScript Komponente im Browser und einer  serverseitigen Komponente.

Als nächstes kommt Scriptaculous.

Auf Notes Seite gibts ja zur Zeit diese Erweiterung von ext zu ext.nd  mit speziellen Domino Erweiterungen. Will ich mir auch noch anschauen, aber zu Prototype gibts einfach die beste Literatur (Prototype and Scriptaculous in Practice von Manning) und ich traue sowieso keinem Bibliothekenschreiber, der mir erzählt, dass mit seiner library alles transparent geregelt ist. Mehr als oft wichtig auch das unter der Haube zu verstehen.

flaite:
Und jetzt hab ich noch was anderes gefixt:
Ein großes Problem von Domino Webanwendungen ist, dass der Code für ein feature sich leicht über verschiedene Stellen verstreut. Gerade die gerne verwendeten openSource DHTML widgets brauchen von sich schon 1 Stylesheet, 1 oder mehrere js Dateien und Initialisierungscode, der dann in Domino auch dadurch zur Zerstreuung neigt, da Leute das als passThru html in die Form posten, weil man da ja computed Text verwenden kann. Schwer zu maintainen.

Nun bestand mein bisheriger JavaScript Code auch aus mehreren Funktionen. Prototype unterstützt aber die Erstellung eigener Klassen, die in JavaScript-Pur recht hacklig ist. Jetzt der neue Code. Die Funktion processTransferin() hätte ich auch lieber in der Klasse. Ist mir aber zur Zeit noch nicht gelungen (ist nicht einfach).

--- Code: ---var CatManager = Class.create();   

CatManager.prototype= {
    initialize:function() {
       
               
  },
 

 
changeCategory: function() {
  ui.transferin = $("transferin");
  var indexSelected = document.forms[0].category.selectedIndex;
  var newCat = document.forms[0].category[indexSelected].text;
  var url = getBaseURL() + "/" + "AjaxController?openAgent"
  var params = "action=changeCategory&cat=" + newCat + "&unid_doc_cur=" + $("unid").innerHTML;
   
    var request = new Ajax.Request(
              url,
  {
                method: 'get',
                parameters: params,
                onComplete: this.parseTransferin,
                onFailure: this.showAjaxError
          }
  );
},           
                   
parseTransferin: function(transport) {
var response = transport.responseText;
var jsonObj = eval("(" + response + ")");
                                       
//ui.transferin.innerHTML = jsonObj.transferout;
$(transferin).innerHTML = jsonObj.transferout;
processTransferin(jsonObj.transferout);

}.bind(this),
                   
    showAjaxError: function(request) {
    alert("error");
    }
 }
 
 var instCatManager = new CatManager()


function processTransferin(valTransferin) {
//var valTransferin = $("transferin").innerHTML;
arrFieldNameValues = valTransferin.split("~~~");
var data = new Object();
var iterator = function(value, index) {
//alert("element " + index + " is " + value);
arrNameValue = value.split("°=°");
if (arrNameValue[0] && arrNameValue.length > 1) {
if (arrNameValue[1].indexOf("µ")) {
arrNameValue[1] = arrNameValue[1].split("µ");
}
data[arrNameValue[0]] = arrNameValue[1];

}
}
arrFieldNameValues.each(iterator);

for (name in data) {
var value = data[name];
if ($(name)) {
$(name).innerHTML = value;
}
}
};

--- Ende Code ---
Zumindest ist es nur 1 Klasse und 1 Funktion und beide können in den JSHeader.

flaite:
Für javascript Entwicklung hat sich Aptana Studio Community definitiv bewährt.
Kostet nix.
Ist Eclipse basiert. 
Bietet eine Menge hilfen beim programmieren wie das automatische Erkennen von Klammern, Unmittelbare Anzeige von Syntaxfehlern, schnelle Tests, Anzeige des Codes in übersichtlichen Strukturen, gute Suchfunktionen und auch Code completion und einen Browser für Funktionen und Klassen.
html und css Unterstützung

http://www.aptana.com/products/studio/community

flaite:
...trotzdem erzeugt das Ajax Abenteuer zur Zeit erstaunliche Mengen an Überstunden...
Scriptaculous hat einen sehr brauchbaren Wiki für Prototype: http://wiki.script.aculo.us/scriptaculous/show/Ajax.Request
Hingegen müssen die vielen deutschen Ajax-Exp3rten-Foren, die bei google aufpoppen noch ein bischen üben. Viel Unsinn. Reg mich nicht mehr drüber auf.
Auf der Domino Seite profitier ich von Zeugs, dens hier in der Firma gibt. Klassen, die serverseitig  CGI-Variablen wie Content_Type und QUERY_STRING_DECODED richtig gut auslesen können machen Sinn, vor allem weil es da auf verschiedenen OS angeblich ein paar gotchas gibt.
Das Errorhandling wird durch die asynchronität ein bischen schwieriger. Wenn nachner Zeit vom Server nix zurückkommt, muss der Client irgendwie reagieren. Und wenn die Session des Users abgelaufen ist, sendet der Server die bekannte html Seite zurück, was sich natürlich schwer bis wirklich garnicht in JSon umwandeln läßt.

Ansonsten:
Libraries wie Prototype/Scriptaculous sind gut
Xml ist gut, aber JSON ist viel, viel besser (für Ajax)
Eine Controller Architektur auf dem Server macht Sinn, d.h. alle Ajax Calls gegen einen Agenten schicken und in dem Call ist ein parameter "action" und dieser Parameter sagt dem Agenten, was das jetzt für eine Anfrage ist. Sowas wird gerne Model-View-Controller genannt, aber das ist übertrieben, weil es kein Modell im eigentlichen Sinne gibt.

Navigation

[0] Themen-Index

[*] Vorherige Sete

Zur normalen Ansicht wechseln