Vorneweg erstmal: XPages sind ein umfangreiches Framework, mit dem sich (komplexe) Web-Anwendungen realisieren lassen, die auf dem Domino-Server gehostet werden. Notes-Datenbanken sind primär Dokumenten-basierte Datenbanken, bieten aus Web-Entwicklungssicht jedoch nur "primitive" Möglichkeiten (mit denen sich jedoch ebenfalls Web-Anwendungen realisieren lassen).
Eine XPages-Applikation kann Daten aus einer Notes-Datenbank verwenden, muss aber nicht. Es können Daten von überall her geholt werden. Eine "Old School" Notes-Datenbank kann das nicht*.
Ein Ajax-Request, gleich welcher Art, benötigt ein "passendes Gegenstück" auf der Serverseite, dass kann aus Sicht einer Notes-Datenbank jedoch alles mögliche sein: Eine Ansicht, ein Agent, eine passende Page, eine Maske... Was auch immer die passende Antwort liefert. XPages bieten neben der Möglichkeit der normalen Ajax-Requests sogenannte "Partial Refreshs", die dazu da sind, definierte Bereiche im Browser neu zu laden, ohne die komplette Seite neu laden zu müssen. Und das Ganze funktioniert dann per Ajax. Als Entwickler muss man sich aber nichts kümmern (senden der Daten an den Server, Generieren der passenden Antwort, austausch des DOM-Baums im Browser) - das macht alles das XPages-Framework.
Zur Grundstruktur:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.data>
<xp:dominoView
var="view1"
viewName="lu.Data">
</xp:dominoView>
</xp:this.data>
<xp:repeat
id="repeat1"
rows="30"
value="#{view1}"
var="rowEntry">
<xp:label value="#{rowEntry.Form}" />
</xp:repeat>
</xp:view>
Da die Daten anscheinend alle in einer Datenbank vorkommen, wäre die wohl einfachste Möglichkeit, eine passende Ansicht zu bauen, die die Daten entsprechend vorbereitet. Diese Ansicht würde auf der XPage dann als Datenquelle im Bereich xp:this.data eingebunden werden. So ist das "M(odel)" vom MVC-Pattern in der Applikation eingebunden.
Die Darstellung der Daten aus der View wird im Element xp:repeat vorgenommen. Das RepeatControl ist eines der großen Errungenschaften für die Notes-Entwicklung, den es besagt, dass der darin enthaltene Code solange neu berechnet werden soll, wie es Werte gibt. Kann man sich als For-Schleife vorstellen, die die Elemente darin beliebig of wiederholt. Die "Werte" sind die einzelnen Einträge der Datenquelle (der Ansicht), und das Element xp:label gibt die Werte der Spalte Form aus. Womit wir das "V(iew)" haben.
Hier werden keine Daten hin und her geschickt, daher fällt das "C(ontroller)" in diesem Beispiel flach.
Wie ist das Zusammenspiel zwischen SSJS und CSJS?
SSJS ist eine Möglichkeit, Code auf dem Server zu generieren. CSJS ist der Code, der im Browser ausgeführt wird. Das Zusammenspiel ist letztlich wie bei einer "normalen" Webseite zu sehen.
Wie ist sowas bei XPages möglich?
Abgesehen von den x-tausend Varianten, die hierbei denkbar sind, wäre eines der einfachsten Beispiele der Pager.
Wenn man folgendes zu dem Beispiel oben hinzufügt...
<xp:pager
layout="Previous Group Next"
partialRefresh="true"
id="pager1"
for="repeat1">
</xp:pager>
... erhält man eine "Blätter"-Funktion, mit der man die einzelnen Seiten der Ansicht per Ajax durchblättern kann.
Ich hoffe, hier ein bischen Licht ins Dunkel gebracht zu haben. Für weitergehende Mentoring-Fragen rund um das Thema "XPages" stehe ich gerne zur Verfügung.
*: Die unglaublichen Workarounds und Krämpfe, die man unternehmen muss, um das doch hinzubiegen (und die in keinem Verhältnis von Aufwand/Nutzen stehen) sind die goldenen Ausnahmen
Vielen Dank für die ausführliche Antwort. Das hilft mir auf jeden Fall weiter.
Also um da nochmal drauf einzugehen, was ich eigentlich machen möchte. Ich habe in meiner Notes-DB mehrere Views. Ich möchte diese nicht als Tabelle/ViewPanel in meiner XPage anzeigen lassen. Vielmehr möchte ich einzelne Werte aus diesen Views auslesen und anschließend in der XPage auf eine bestimmte Art und Weise darstellen lassen (man könnte das auch als Dashboard bezeichnen). Das heißt ich muss zu Beginn die Verbindung zu meiner Notes-DB aufbauen, die Views ansprechen, durchgehen und einzelne Werte in Variablen speichern. Diese sollten dann über Dojo oder jQuery in die XPage an bestimmte Stellen eingefügt werden. Das wäre der erste Schritt, ganz ohne Ajax oder dergleichen. Wie würde hierfür die Grundstruktur aussehen? Mache ich das auf Client- oder Server-Seite?
Zum anderen soll dann im zweiten Schritt auf klick-Events reagiert werden. Dabei soll nach dem Klick wieder andere Werte aus anderen Views aus meiner Notes-DB ausgelesen und in die XPage eingefügt werden (ohne diese neu zu laden).
Also es geht mir wirklich nur um einzelne Werte (beispielsweise über einen ViewNav die View durchlaufen um bestimmte Werte auszulesen) aus verschiedenen Views in die XPage zu integrieren. Die Werte aus den Views sollen über das Javascript in die BOM der XPage eingefügt werden. Keine gesamte View oder ähnliches. Dementsprechend wird auch kein Pager benötigt. Ich hoffe das ist verständlich, aber falls Du dazu noch Fragen hast, nur zu :)
Thanks in advance...
EDIT:
Also so in etwa habe ich mir das vorgestellt für den ersten Schritt:
<xp:text escape="true" id="computedField1">
<xp:this.value><![CDATA[#{javascript:
var db = session.getCurrentDatabase();
nav = db.getView("myview").createViewNav();
entry = nav.getFirst();
value = entry.getColumnValues()[2];
return value;
}]]></xp:this.value>
</xp:text>
Allerdings bin ich mir nicht sicher, ob das bei mehreren Computed Fields so effizient ist.
EDIT2:
Ich glaube ich habe gefunden, was ich gesucht habe. Zumindest kommt es dem sehr nahe. Und zwar die Möglichkeit SSJS im CSJS auszuführen. Kombiniert mit einem click-Event sollte es etwa das sein, wonach ich gesucht hatte:
$("#test").click(function() {
var test = #{javascript:
var db = session.getCurrentDatabase();
nav = db.getView("myview").createViewNav();
entry = nav.getFirst();
value = entry.getColumnValues()[2];
return value;
};
$("#test").html(test);
});
Das heißt ich muss zu Beginn die Verbindung zu meiner Notes-DB aufbauen, die Views ansprechen, durchgehen und einzelne Werte in Variablen speichern
Warum glaubst Du, dass Du das musst?
Mache ich das auf Client- oder Server-Seite?
Du willst im Client einen Button anklicken, der Daten vom Server abholt, und im Client darstellt. Ergo auf beiden Seiten.
Also so in etwa habe ich mir das vorgestellt für den ersten Schritt:
Du meinst so etwas (Um das vorherige Beispiel wieder auzugreifen)? Nutzt eine Datasource. Auf der XPage kann es beliebig viele davon geben.
<xp:text escape="true" id="computedField1" value="#{javascript:rowEntry.getColumnValues().get(0)}" />
Allerdings bin ich mir nicht sicher, ob das bei mehreren Computed Fields so effizient ist.
Ist extrem ineffizient.
Ich glaube ich habe gefunden, was ich gesucht habe. Zumindest kommt es dem sehr nahe.
Dein SSJS Code wird immer ausgeführt, und es hat nichts mit dem OnClick-Event im Client zu tun. Schau Dir mal den generierten HTML-Code im Browser an. Dein Code wird nur im Client ausgeführt, die Daten stehen dem Client zur Verfügung. Da wird nix vom Server geladen; Dein Vorhaben, "andere" Daten aus anderen Views zu laden, funktioniert so nicht.
Du meinst so etwas (Um das vorherige Beispiel wieder auzugreifen)? Nutzt eine Datasource. Auf der XPage kann es beliebig viele davon geben.
<xp:text escape="true" id="computedField1" value="#{javascript:rowEntry.getColumnValues().get(0)}" />
Genau, so etwas meine ich.
Dein SSJS Code wird immer ausgeführt, und es hat nichts mit dem OnClick-Event im Client zu tun. Schau Dir mal den generierten HTML-Code im Browser an. Dein Code wird nur im Client ausgeführt, die Daten stehen dem Client zur Verfügung. Da wird nix vom Server geladen; Dein Vorhaben, "andere" Daten aus anderen Views zu laden, funktioniert so nicht.
Ich würde mich sehr freuen, wenn Du dazu erklären würdest wie mein Vorhaben denn funktionieren würde.
So sieht das ganze mittlerweile bei mir aus (nach diesem Beispiel (http://planetlotus.org/profiles/karl-henry-martinsson_123389)):
$("#button").click(function(e) {
request = $.ajax({
url: "http://meinserver.com/meindirectory/meinedatenbank.nsf/ajaxHandler?OpenAgent",
cache: false,
dataType: "html"
});
request.done(function(data) {
$("#text").html(data);
});
request.fail(function( jqXHR, textStatus ) {
alert( "Request failed: " + textStatus );
});
});
Der Agent sieht erstmal nur wiefolgt aus:
Sub Initialize
Dim result As String
result = "Hello World!"
Print result
End Sub
Allerdings erhalte ich auch hier immer eine Fehlermeldung. Aber vielleicht kann man darauf aufbauen?
hier mal ein Beispiel - Variablen sind anzupassen -
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" dojoParseOnLoad="true"
dojoTheme="true">
<xp:this.data>
<xp:dominoView var="view1" viewName="Alle Vorgänge"></xp:dominoView>
<xp:dominoDocument var="document1" formName="test"></xp:dominoDocument>
</xp:this.data>
<xp:button value="Spalte 1 holen" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="partial" refreshId="viewPanel">
<xp:this.action><![CDATA[#{javascript:viewScope.SpaltenTitel = "Kennzeichen"}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
     
<xp:button value="Spalte 2 holen" id="button2">
<xp:eventHandler event="onclick" submit="true"
refreshMode="partial" refreshId="viewPanel">
<xp:this.action><![CDATA[#{javascript:viewScope.SpaltenTitel = "erstellt"}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:panel id="viewPanel">
<xp:repeat id="repeat1" rows="30" var="rowData" indexVar="repeatList"
value="#{view1}">
<xp:table>
<xp:tr>
<xp:td>
<xp:label id="label1">
<xp:this.value><![CDATA[#{javascript:rowData.getColumnValue(viewScope.SpaltenTitel);
}]]></xp:this.value>
</xp:label>
</xp:td>
<xp:td></xp:td>
</xp:tr>
<xp:tr>
<xp:td></xp:td>
<xp:td></xp:td>
</xp:tr>
</xp:table>
</xp:repeat>
</xp:panel>
</xp:view>
Vielen Dank für die Antwort. Bitte korrigiere mich, wenn ich falsch liege. Aber so wie ich das sehe ist das wieder eine gesamte Viewpanel, die aktualisiert wird. Ich möchte ja nur einzelne Datenpunkte aus der Datenbank holen und keine View anzeigen...
Ich habe das ganze nochmal mit Dojo probiert (nach diesem Beispiel (http://dojotoolkit.org/documentation/tutorials/1.6/ajax/)), wieder nur error...
dojo.xhrGet({
url: "http://meinserver.com/directory/database.nsf/ajaxHandler?OpenAgent",
load: function(result) {
alert("The message is: " + result);
},
error: function() {
alert("error");
}
});
So langsam vergeht mir die Lust an XPages. Eigentlich wollte ich mich nochmal reinarbeiten, aber im Vergleich zur "herkömmlichen" Entwicklung mit HTML, Javascript, PHP etc. ist das einfach nur ein Graus. Oder ich bin einfach zu blöd dafür.
Acl - maximaler Internetzugriff unter erweitert zu gering...
Okay, danke für den Tipp. Ist jetzt auf "Editor" gesetzt. Die Views kann ich nun anschauen, bei einem Versuch einen Agent zu öffnen (http://server.com/directory/database.nsf/myAgent?OpenAgent) bekomme ich dennoch weiterhin diese Fehlermeldung:
Error 500
HTTP Web Server: Lotus Notes Exception - Error validating user's agent execution access
Ich bin mittlerweile bei dieser Lösung angekommen:
<xp:button id="button" value="button" style="display:none;">
<xp:eventHandler event="onclick" submit="true"
refreshMode="partial" refreshId="computedField">
<xp:this.action><![CDATA[#{javascript:
var db = session.getCurrentDatabase();
nav = db.getView("myview").createViewNav();
entry = nav.getLast();
value = entry.getColumnValues()[0];
viewScope.test = '<p>Value:</p><h1>'+value+'</h1>';
}]]></xp:this.action>
<xp:this.onComplete>
<![CDATA[$("#computedField").show();]]>
</xp:this.onComplete>
<xp:this.onError><![CDATA[alert("error")]]></xp:this.onError>
</xp:eventHandler>
</xp:button>
Wenn ich das richtig sehe wird der SSJS-Code auch wirklich erst beim Click-Event ausgeführt. Beim computedField habe ich die Darstellung auf "HTML" umgestellt (bzw. escape=false). Oder bin ich wieder auf dem Holzweg?
Ansonsten habe ich jetzt noch einen Versuch mit Ajax gestartet. Wenn ich im Browser eine URL in diesem Format eingebe
http://server.com/dir/database.nsf/myview?ReadViewEntries&OutputFormat=JSON&count=300
bekomme ich das korrekte Ergebnis angezeigt. Wenn ich eine Ajax Request an diese URL schicke erhalte ich wieder einen Error (Error type = 0 -> No connection)...Woran kann das jetzt wieder liegen?
request = $.ajax({
url: "http://server.com/dir/database.nsf/myview?ReadViewEntries",
data: {Count:"300",OutputFormat:"JSON"},
dataType: "json",
beforeSend: function(jqXHR, settings) {
$("#url").html(settings.url);
},
error: function(jqXHR, exception) {
if (jqXHR.status === 0) {
alert('Not connect.\n Verify Network.');
} else if (jqXHR.status == 404) {
alert('Requested page not found. [404]');
} else if (jqXHR.status == 500) {
alert('Internal Server Error [500].');
} else if (exception === 'parsererror') {
alert('Requested JSON parse failed.');
} else if (exception === 'timeout') {
alert('Time out error.');
} else if (exception === 'abort') {
alert('Ajax request aborted.');
} else {
alert('Uncaught Error.\n' + jqXHR.responseText);
}
}
});
request.done(function(data) {
$("#text").html(data);
});
Bei beforeSend habe ich mal den konstruierten URL-String ausgeben lassen, der passt.