Entwickeln von Windows DNA-Anwendungen mit dem COMCodebook – Teil 9 Yair Alan Griver Übersetzt von Mathias Gronau Teil 9 – Kontrolle der Präsentation mit clientseitigem JavaScript In diesem Abschnitt behandeln wir, wie wir unsere Präsentation mit clientseitigem JavaScript kontrollieren können. Ich werde Ihnen zeigen, wie Sie Ihre Fenster verwalten und neu aufbauen. Außerdem zeige ich Ihnen eine einfache Prüfroutine auf Feldebene. Wir benutzen für unsere Aufgaben das DHTML-Objektmodell des Browsers und clientseitiges JavaScript. Nähere Informationen über DHTML unter dem Internet Explorer finden Sie im Abschnitt DHTML Object Model im MSDN Web Workshop auf http://msdn.microsoft.com/workshop/ unter dem Stichwort DHTML, HTML & CSS. Eine gute Referenz für JavaScript finden Sie unter http://developer.netscape.com/docs/manu als/communicator/jsref/index.htm. Verwalten Browsers der Fenster des Meiner Meinung nach ist die Verwaltung der Fenster in Web-Anwendungen genau so wichtig wie die Präsentation und die Grafik. Es ist eine Katastrophe, auf eine Website zu kommen, die zwölf Fenster öffnet und sie nicht wieder schließt. In diesem Teil behandle ich die Kontrolle der Fenster und wie sie sich darauf auswirkt, wie der Anwender die Website wahrnimmt. Ich habe Ihnen im Unterverzeichnis jscripts, das Sie unterhalb Ihres Verzeichnisses Interface finden, einigen JavaScript-Code bereitgestellt. 12-19 Entwickeln von Windows DNA/Teil 9 Öffnen eines zusätzlichen Fensters Wir wollen automatisch ein kleineres Fenster für die Bemerkungen öffnen, wenn der Anwender eine Bemerkung auswählt. Dies kann er entweder im Notizgrid oder im Anwaltsformular tun. Wir erledigen unsere Aufgabe durch den Aufruf der JavaScriptFunktion openNote(). Die Funktion finden Sie in der Datei notelist.js unseres Unterverzeichnisses jscripts. Um die Datei zu öffnen, klicken Sie im Windows Explorer mit der rechten Maustaste daraus und wählen Sie aus dem Kontextmenü „Edit“. Hier die JavaScript-Funktion: function openNote(goURL) { window.open(goURL,'','width=550,height =365,scrollbars=yes,location=no,left=1 0,top=10,menubar=no,resizable=yes,tool bar=no,status=yes'); } Diese Funktion öffnet einfach eine weitere Instanz des Browserfensters auf die URL, die ihr als Parameter goURL übergeben wurde. Die Methode window.open() des Browsers akzeptiert als Parameter die URL und einen String mit Attributen, die das Aussehen des Fensters beschreiben. Ich habe z. B. die Größe des Fensters auf eine Breite von 550 Pixel und eine Höhe von 365 Pixel eingestellt. Außerdem habe ich angegeben, dass die Toolbar (menubar=no), das Menü (menubar=no) und die Adresszeile (location=no) nicht angezeigt werden sollen. Der Aufruf dieser Funktion befindet sich im Hyperlink der Bemerkungsliste. Wollen wir beispielsweise die Suchergebnisse generieren, platzieren wir FoxX Professional Seite 1 die folgende Codezeile in unserer Active Server Page: Response.Write("<a href=""Javascript:openNote('AttorneyNo te.asp?Action=List&ID=" & rs("unique_id") & "')"">" & rs("note_time") & "</a>") Damit wird der folgende HTML generiert: <a href="Javascript:openNote('AttorneyNot e.asp?Action=List&ID=001')">3/20/00 12:57:00 PM</a> Wenn jetzt der Anwender auf den Hyperlink klickt, öffnet die Funktion openNote() ein neues Fenster des Browsers und navigiert die Inhalte an AttorneyNote.asp. Um aus HTML heraus JavaScript-Funktionen nutzen zu können, die sich in separaten Dateien befinden, platzieren wir im Header der HTML-Seite den folgenden Code: <head> <title></title> <script LANGUAGE="JavaScript" SRC="jscripts/notelist.js"></script> </head> Beachten Sie, dass das Attribut source den relativen Pfad zur JavaScript-Datei angibt. Damit wird der Browser angewiesen, die Datei notelist.js mit der HTML-Seite aus dem Verzeichnis jscripts herunterzuladen. Automatisches Fensters Note Schließen des Jetzt wissen wir, wie wir automatisch ein kleineres Fenster öffnen, wenn der Anwender im Formular Attorney eine Bemerkung auswählt. Nun ist es unsere Aufgabe, das Fenster wieder zu schließen, wenn der Anwender seine Aktion beendet hat. Wenn Sie processNote.asp öffnen, werden Sie feststellen, dass nach jeder Aktion ein Aufruf von Response.Redirect(“closewindow.htm“) folgt. Damit wird an den Browser die Aufforderung gesendet, mit etwas JavaScript-Code das Fenster zu schließen. Sie können closewindow.htm wahlweise in Visual InterDev oder in Notepad öffnen. Werfen Sie einen Blick auf das HTML: <html> 12-19 Entwickeln von Windows DNA/Teil 9 <head> <title></title> </head> <body onload="window.close()"> </body> </html> Der aktive Teil dieses HTML-Formulars ist der Aufruf von window.close() im Ereignis onload des Dokuments. Er bewirkt, dass der Browser das Fenster automatisch schließt. Sie können ein sekundäres Fenster ohne Nachfragen des Browsers schließen. Als sekundäres Fenster bezeichne ich ein Fenster, dass vom Hauptfenster aus geöffnet wird. In unserem Fall ist das Formular Attorney das Hauptfenster und das Formular note wird in einem sekundären Fenster geöffnet. Wenn Sie versuchen das letzte Hauptfenster zu schließen gibt der Browser eine Warnung aus und fragt nach, ob es in Ordnung ist, dass die Anwendung den Browser beendet. Das Schließen des Hauptfensters ist genau so ärgerlich wie das Öffnen von einem Dutzend Fenstern ohne diese wieder zu schließen. Neuzeichnen (FrameNotes) des Grid Note Das Neuzeichnen des Grid Note, unabhängig vom Formular Attorney, wird durch den Dynamic HTML-Tag <IFRAME> ermöglicht. Die Inline-Frames geben uns die Möglichkeit, die Liste der Bemerkungen vom Formular zu separieren. Das ist sinnvoll, wenn wir unterschiedliche Teile des Formulars neu anzeigen wollen oder dem Anwender die Möglichkeit geben wollen, Änderungen an den Bemerkungen vorzunehmen, während der Anwalt noch nicht gespeicherte Modifizierungen enthält. Wenn der Anwender Änderungen an einer Bemerkung vornimmt oder diese löscht, wollen wir nur den Grid neu anzeigen, nicht das gesamte Formular. Wenn der Anwalt als übergeordneter Datensatz noch nicht gespeicherte Änderungen enthält, wollen wir diese Daten nicht noch einmal vom Server abfragen und die Änderungen dabei verwerfen. Ich habe festgestellt, dass die Anwender das nicht so sehr mögen. Wir können nur das Grid neu anzeigen, indem wir die JavaScript-Funktion callerRefresh FoxX Professional Seite 2 aufrufen. Diese Funktion befindet sich in interface/jscripts/winman.js. Um die Datei zu öffnen klicken Sie sie mit der rechten Maustaste an und wählen aus dem Kontextmenü „Öffnen“. Hier die JavaScript-Funktion: function callerRefresh(oReload){ if (oReload != null) oReload.location.reload(true) return true ; } Damit wird einfach das Windowsobjekt, das der Funktion übergeben wurde, neu geladen. Wir rufen diese Funktion im Ereignis onunload des Formulars AttorneyNote.asp auf. Wenn Sie AttorneyNote.asp in Visual InterDev öffnen, werden Sie feststellen, dass das Ereignis onunload aufgrund weniger Bedingungen auf dem Server generiert wird: <body class="dataform" <% if cID = "" and cAction = "" then 'If we are adding, we came from the container 'so we must specify the frame (grid) to refresh by name Response.Write("onunload=callerR efresh(window.opener.frames(""FrameNot es""));") else 'Don’t refresh the Attorney List if cAction <> "List" then 'We are editing, we came from inside the grid itself Response.Write("onunload=callerR efresh(window.opener);") end if end if %> > Als erstes müssen wir prüfen, ob der Anwender über den Link „Add Note“ vom Formular cntAttorney.asp kommt. Ist das der Fall, müssen wir „FrameNotes“ neu zeichnen. Der Name des Frame befindet sich im Attribut name des Tag <IFRAME>. Wenn wir eine Bemerkung ändern wollen bedeutet dies, dass wir entweder von den Abfrageresultaten oder vom Grind mit den Bemerkungen kommen. Kommen wir von den Abfrageresultaten, enthält die Variable cAction das Wort „List“ und wir müssen keinen Code generieren. Andernfalls kommen wir vom Grid mit den 12-19 Entwickeln von Windows DNA/Teil 9 Bemerkungen und window.opener referiert auf den Grid. Beide Fensterobjekte verweisen auf den gleichen Grid mit den Bemerkungen. Je nachdem von welchem Frame aus wir das Bemerkungsfenster geöffnet haben, verweisen wir unterschiedlich darauf. Dieser Code zeichnet einfach den Grid mit den Bemerkungen (FrameNotes) neu. Alle Frames auf dem Formular Attorney verwalten Ich habe bereits weiter oben angemerkt, dass wir einige Tricks einsetzen müssen, wenn wir unser Containerformular neu zeichnen wollen, besonders wenn wir vom EditModus in den Add-Modus gehen. Wenn der Anwender auf die Schaltfläche „Add“ des Hauptformulars klickt, wird das Formular an processAttorney.asp gesandt, das im CASEBlock das Hinzufügen abhandelt. Wenn Sie processAttorney noch einmal öffnen werden Sie bemerken, dass im Fall „Add“ kein Code steht. Das liegt daran, dass processAttorney.asp beim Hinzufügen einige Besonderheiten ausführt. Da unser Formular Attorney in einem Frame von cntAttorney.asp enthalten ist, ist es erforderlich, den Frame „aufzubrechen“ wenn wir einen neuen Anwalt anlegen wollen. Das liegt daran, dass, wenn wir in aus einem bestehenden Datensatz in den Add-Modus wechseln, wir den Grid mit den Bemerkungen auf der unteren Hälfte des Formulars „löschen“ müssen. Die einzige Möglichkeit dafür ist das Neuzeichnen des übergeordneten Containerfensters (cntAttorney). Wir wollen keine untergeordneten Datensätze mit Bemerkungen anzeigen, wenn wir einen neuen Anwalt anlegen, zumindest nicht, so lange der Anwalt nicht gespeichert ist und wir anschließend den Grid anzeigen. Es ist nicht einfach, das Frontend vom Server aus mit einem response.Redirect() auf einen speziellen Frame umzuleiten. Wir müssen diese Aufgabe durch den Browser erledigen lassen. Am Ende von processAttorney.asp finden Sie ein Stück JavaScript-Code, das den Browser auf die richtige Seite umlenkt, indem es den äußeren Frame (top.location) FoxX Professional Seite 3 einstellt. Diesen Weg müssen wir gehen, da Attorney.asp sich in einem Frame auf der Seite cntAttorny.asp befinden würde. Wenn wir den Anwender innerhalb des Frame umleiten würden, würde lediglich der Inhalt des Frames geändert. Das bedeutet, dass, wenn wir einen Anwalt und seine Bemerkungen ändern und anschließend einen neuen Anwalt anlegen, der Frame Attorney in den Add-Modus wechseln würden, wir aber immer noch die Bemerkungen zum vorhergehenden Anwalt sehen würden. Die Verwaltung der Frames kann schon trickreich sein. Es kann dazu führen, dass Sie zur Laufzeit Frames innerhalb anderer Frames haben. Würde der Frame Attorney Sie auf cntAttorney.asp umleiten würden, Sie sich plötzlich in einem anderen Container Attorney (mit Frames und allem anderen) innerhalb des Frame Attorney befinden, das den Container aufgerufen hat. Da wir mit der Methode Response.Redirect den Anwender nicht zu einem speziellen Frame umleiten können, müssen wir etwas JavaScript-Code entwickeln, das an den Client gesendet wird und dort top.location mit der ASP-Seite abgleicht. Sehen Sie sich den Fuß der Seite processAttorney.asp an. Beachten Sie, wie das serverseitige Script das clientseitige Script generiert: <html> <head> <title></title> <% Response.Write("<script language='JavaScript'>") Response.Write("function breakframes(){ ") Response.Write("if ( top.location != location ) ") Response.Write("top.location.href = ") Select Case cAction Case "Add" Response.Write("'cntAttorney.asp ?Action=Add' ; ") Case "SaveNew" 'We just saved a new record Response.Write("'cntAttorney.asp ?ID=" & cID & "' ; ") Case "Cancel" Response.write("'default.asp' ;") Case "Delete" 12-19 Entwickeln von Windows DNA/Teil 9 Response.write("'default.asp' ;") Case Else Response.write("'default.asp' ;") End Select Response.Write(" } </script> ") %> </head> <body onload='breakframes()'> </body> </html> Am Ende wird folgendes generierte HTMLFormular an den Client gesendet (wenn der Anwender auf „Add“ geklickt hat): <html> <head> <title></title> <script language='JavaScript'> function breakframes(){ if ( top.location != location ) top.location.href = 'cntAttorney.asp?Action=Add' ; } </script> </head> <body onload='breakframes()'> </body> </html> Damit wird, zusammenfassend ausgedrückt, die Containerseite (cntAttorney.asp) zur angegebenen Seite (cntAttorney.asp?Action=Add) umgelenkt. Hätten wir die Containerseite nicht umgelenkt und statt dessen aus processAttorney.asp heraus ein normales response.Redirect durchgeführt, würden wir uns in einer anderen Containerseite innerhalb des Frame Attorney wiederfinden. cntAttorney.asp ruft unsere Seite Attorney.asp mit dem korrekten QueryString auf. Dies ist ein schönes Beispiel für die bedingte Generierung eines clientseitigen Scripts auf dem Server. Eine einfache Prüfroutine mit JavaScript Nachdem Sie nun Fenster mit Hilfe von JavaScript verwalten können, möchte ich Ihnen zeigen, wie wir mit JavaScript eine einfache Prüfung der Daten erstellen FoxX Professional Seite 4 können. Wir schreiben daher eine einfache Prüfroutine für unser Formular Attorney, so dass wir für diesen Zweck keine Eingriffe in unsere Komponente AttorneyValidator vornehmen müssen. Wir sparen damit einen Zugriff auf den Server. Wir wollen sicherstellen, dass der Nachname des Anwalts nicht leer ist. Dafür entfernen wir zuerst die Leerzeichen am Anfang und am Ende des String und prüfen das Resultat, ob es sich von einem leeren String unterscheidet. Ist dies nicht der Fall, zeigen wir dem Anwender eine Meldung an und verhindern die Speicherung des Datensatzes. Außerdem schreiben wir alle Zeichen im Adressfeld State in Großbuchstaben. Als erstes müssen wir in unserem Unterverzeichnis \jscripts eine JavaScriptDatei anlegen. Sie können einfach ein neues Textdokument anlegen und es später umbenennen, damit es die Dateinamenserweiterung .js besitzt. Nennen wir die Datei vattorney.js. Wenn Sie mit der rechten Maustaste auf die Datei klicken, können Sie im Kontextmenü „Öffnen“ auswählen und sie sollte in Notepad geöffnet werden. Unsere Funktion wollen wir validate() nennen. Geben Sie im Editor folgendes ein: function validate(thisform) { } Wir müssen dieser Funktion den Namen des zu prüfenden Formulars übergeben. Funktionen beginnen in JavaScript immer mit der öffnenden geschweiften Klammer ({) und enden mit der schließenden geschweiften Klammer (}). Wir wollen von der Funktion ein .T. oder .F. zurückerhalten. Wird .F. zurückgegeben bedeutet das, dass das Feld Nachname leer ist und das Formular wird nicht gesendet. Kommt ein .T. zurück bedeutet das, dass alles in Ordnung ist und wir können weitermachen und dem Anwender die Möglichkeit geben, das Formular zu senden. Legen wir also drei Variablen an, eine für den Rückgabewert (llRetVal), eine für den Wert des Nachnamens und eine für den Wert des Feldes cState. Unsere Funktion sieht jetzt folgendermaßen aus: 12-19 Entwickeln von Windows DNA/Teil 9 function validate(thisform) { var llRetVal = true; var cLastName = thisform.lastname.value; var cState = thisform.state.value; return llRetVal } Jetzt können wir eine IF-Anweisung schreiben, die einfach prüft, ob der Nachname leer ist und, wenn dies der Fall ist, eine Meldung ausgibt: if (thisform.lastname.length == 0) { alert("Please enter a last name."); thisform.lastname.focus(); llRetVal = false; } Außerdem wird hier dem Feld mit dem Nachnamen der Fokus zurückgegeben. Das Problem mit dieser IF-Anweisung ist, dass sie auch ein .T. zurückgibt, wenn der Anwender nur Leerzeichen in das Feld eingibt. Es ist dann technisch nicht leer. Wir müssen also die führenden und beendenden Leerzeichen entfernen und anschließend prüfen, ob der String leer ist. Dafür rufen wir in JavaScript einen „regulären Ausdruck“ auf. Ein gutes Tutorial „Regular Expressions in JavaScript 1.2“ von Neeraj Kochhar finden Sie im Internet unter http://www.myriadvoices.com/JavaScript/ RegularExpressions/regular_expression_in_j avascript_pg1.htm. Wir entwickeln einen regulären Ausdruck, der nach Leerzeichen (einschließlich Tabs und Returns) sucht. Dafür benutzen wir das spezielle Zeichen \s. Um den regulären Ausdruck zu erstellen, geben wir den folgenden Code ein: var re_LastName = /\s+/; Die Muster der regulären Ausdrücke stehen immer zwischen Schrägstrichen (/). /s ist ein Sonderzeichen, das den Leerzeichen (einschließlich Tabs und Returns) entspricht. Das Zeichen „+“ zeigt an, dass die Leerzeichen im gesamten String gesucht werden sollen. Wir müssen die JavaScript- FoxX Professional Seite 5 Methode replace aufrufen, um den String cLastName von allen Leerzeichen zu befreien: if (cLastName.replace(re_LastName, "") == "") { alert("Please enter a last name."); thisform.lastname.focus(); llRetVal = false; Sie sollten in der Lage sein, Ihre eigenen regulären Ausdrücke zu erstellen, die z. B. das korrekte Format der Telefonnummern oder der Staatencodes in den USA prüfen, oder um Funktionen wie RTRIM(), LTRIM(), oder ALLTRIM() zu erstellen. Wollten wir beispielsweise die Staatencodes anhand einer Liste gültiger Codes prüfen könnten wir einen regulären Ausdruck wie diesen erstellen: var re_state = /^A[KLRZ]$|^C[AOT]$|^D[CE]$|^FL$|^GA$| ^HI$|^I[ADLN]$|^K[SY]$|^LA$|^M[ADEINOS T]$|^N[CDEHJMVY]$|^O[HKR]$|^PA$|^RI$|^ S[CD]$|^T[NX]$|^UT$|^V[AT]$|^W[AIVY]$/ i; In unserer einfachen Anwendung beschränken wir uns aber darauf, die Staaten durchgängig in Großbuchstaben zu schreiben. Dafür rufen wir im Stringobjekt cState die Methode toUpperCase() auf: 12-19 Entwickeln von Windows DNA/Teil 9 function validate(thisform) { var llRetVal = true; var cLastName = thisform.lastname.value; var cState = thisform.state.value; var re_LastName = /\s+/; if (cLastName.replace(re_LastName, "") == "") { } thisform.state.value = cState.toUpperCase(); Damit haben wir vattorney.js fertiggestellt. Sie sollte folgendermaßen aussehen: alert("Please enter a last name."); thisform.lastname.focus(); llRetVal = false; } thisform.state.value = cState.toUpperCase(); return llRetVal } Diese Funktion rufen wir im Ereignis onclick der Schaltfläche „Save“ des Formulars Attorney.asp auf. Wir müssen noch die Referenz auf unser Script dem HTML-Header hinzufügen. Öffnen Sie dafür Attorney.asp in Visual InterDev. Fügen Sie dem HTML-Header die hier fett gedruckte Zeile hinzu: <head> <title><% =cHeader%> </title> <link REL="stylesheet" TYPE="text/css" HREF="style.css" /> <script LANGUAGE="JavaScript" SRC="jscripts/vattorney.js"></script> </head> FoxX Professional Seite 6 Jetzt müssen die Funktion nur noch aus unserer Schaltfläche heraus aufrufen. Fügen Sie folgenden Code der Schaltfläche „Save“ hinzu: <input type="submit" value="Save" name="cmdAction" onclick="return(validate(window.thisfo rm))"> Wir senden den Namen unseres Formulars an die Prüffunktion. Der Name des Formulars wird im Tag <FORM> angegeben. Lassen Sie die Anwendung laufen und wählen Sie einen Anwalt zum Ändern aus. Füllen Sie den Nachnamen mit Leerzeichen und klicken auf „Save“. Sie sollten die folgende Mitteilung sehen: Wenn Sie auf OK klicken, erhält das Feld Last Name den Fokus und das Formular wird nicht gesendet. Die Verwendung von JavaScript auf dem Client ist hier sinnvoll, da auf diese Weise kein Zugriff auf die mittlere Schicht erforderlich ist, wodurch Zeit und Serverressourcen gespart werden. Wenn Sie mit JavaScript noch nicht vertraut sind, werden Sie eventuell überwältigt sein, wie wenig Code Sie für unsere einfache Anwendung schreiben müssen. Die Kontrolle des Browsers sollte nicht auf die leichte Schulter genommen werden. Zum 12-19 Entwickeln von Windows DNA/Teil 9 Glück gibt es im Internet viele Informationen über Browser und JavaScript. Wenn Sie eine Funktion benötigen, haben Sie gute Chancen, dass bereits jemand diese Funktion geschrieben hat und dass Sie sie irgendwo finden. Es benötigt einige Zeit, eine neue Sprache zu lernen. Wenn Sie mehrere Webanwendungen entwickelt haben werden Sie feststellen, dass JavaScript und VBScript einfach einzusetzen und sehr mächtig sind. Schluss mit der Präsentation Sie sollten jetzt über eine einsatzfähige ASPAnwendung verfügen, deren Komponenten der mittleren Schicht mit Visual FoxPro und dem COMCodebook entwickelt wurden. Hinter der Anwendung liegt eine Visual FoxPro-Datenbank. Wir haben mit HTML und CSS gearbeitet und serverseitiges VBScript wie auch clientseitiges JavaScript eingesetzt. Damit haben wir eine arbeitende (und gut aussehende) Internetanwendung erstellt. Einen Schwerpunkt haben wir auf die Präsentation gelegt. Nachdem wir diese Aufgabe erledigt haben, werden wir jetzt die Werkzeuge wechseln und unsere Datenbank auf eine Oracle- oder SQL ServerDatenbank upgraden. Außerdem werden wir uns mit der Zusammenarbeit unserer Komponenten mit dem Microsoft Transaction Server beschäftigen. FoxX Professional Seite 7