Bob Swart (aka Dr.Bob)
Delphi 5 InternetExpress (II)

Delphi 5 InternetExpress combineert de WebBroker technology met de MIDAS technology. Het eindresultaat zijn ultra-thin clients van HTML met XML en JavaScript. Vorige keer liet ik zien dat de InternetExpress technology zelfs in een 4-tier application resulteert: SQL database server, middle-ware data server (MIDAS), web server (WebBroker) en tenslotte de HTML/XML in de web browser. Deze keer zullen we zien dat we de eerste drie van deze lagen kunnen combineren in n (een web server toepassing).

WebBroker: ISAPI
We beginnen deze keer dus niet met het maken van een MIDAS Server, maar meteen met het starten van een Web Server Application (uit de pagina "New" van de Object Repository). Kies hierbij voor een ISAPI/NSAPI Dynamic Link Library. Overigens hoorde ik laatst dat Netscape van plan is om de ontwikkeling aan NSAPI stop te zetten, en in de toekomst ook ISAPI te ondersteunen. Dat is goed nieuws, want dan komen de technieken weer wat dichter bij elkaar. Aan de andere kant is het voor WebBroker ontwikkelaars toch niet zo heel boeiend, omdat de code die we voor de Web Module moeten schrijven toch hetzelfde blijft of we nu met ISAPI, NSAPI, CGI of zelfs WinCGI zouden moeten eindigen. Maar goed, we hebben dus een ISAPI DLL - die is in ieder geval makkelijk te debuggen, zoals we straks zullen zien.

Bibliotheek
De vorige keer beloofde ik het al: een master-detail relatie. Maar in plaats van de overbekende customer/orders/items maak ik liever gebruik van twee geheel nieuwe tabellen (die je bijvoorbeeld met de Database Desktop kunt maken). De eerste tabel bevat "Medewerker" informatie, en heeft in ieder geval een Medewerker (unieke ID key) en een MedewerkerNaam als velden. De tweede tabel is de "Bibliotheek", en deze heeft in ieder geval een veld Medewerker (ID van diegene die het boek uitgeleend heeft), een veld Titel en een veld Uitleendatum. Meer mag, maar is niet perse nodig voor de rest van het verhaal deze keer. Het veld Medewerker is key van de Medewerk.db tabel, en heeft een secundaire index (niet uniek) op de Bieb.db tabel.
We kunnen de WebModule van ons ISAPI project gebruiken om de twee tabellen neer te zetten en de master-detail relatie te leggen. Zoals we de vorige keer zagen, krijgen we meteen een rode rand om de TTable components heen zodra we ze op een data module (of web module) zetten. Dit gaat pas over nadat we de Alias (ATwhere) en Tablenames (medewerk.db en bieb.db) hebben opgegeven. We kunnen de tabellen nu naar de Data Diagram tab slepen, en met de rechter muisknop alle velden toevoegen die we nodig hebben. Het resultaat ziet er als volgt uit:

medewerk.db en bieb.db

Zoals hierboven te zien is, bestaat de medewerker tabel naast de Medewerker key ook uit de Voornaam en de Achternaam (als aparte velden). Nu zijn we bij TAS-AT nog niet zo groot, maar we zijn wel aan het groeien, dus binnenkort is het niet meer voldoende om het alleen maar over iemands voornaam (of achternaam) te hebben. En ook de eerste letter van de voornaam bij de achternaam optellen werkt niet als we het TAS breed trekken, want er zijn al twee personen binnen de TAS Groep die R.Pels heten (om over A.Jansen nog maar te zwijgen). Kortom, we hebben een extra veld nodig dat gewoon de volledige voornaam bij de achternaam optelt. Een Calculated field, met als naam "Naam", als type "String" (lengte 80), en de OnCalcFieldsEvent van de TableMedewerker ziet er daarbij als volgt uit:

  procedure TWebModule1.TableMedewerkCalcFields(DataSet: TDataSet);
  begin
    TableMedewerkNaam.AsString :=
      TableMedewerkVoornaam.AsString + #32 +
      TableMedewerkAchternaam.AsString
  end;
Waarom zo'n moeite voor een nieuw veldje met de volledige naam? Welnu, we zullen dit nieuwe veld straks kunnen gebruiken op een medewerker te selecteren (en zoals wel vaker in "real life" is niet altijd alles al bij voorbaat voorgebakken).

Master-Detail
Voor het leggen van de master-detail relatie moeten we nog een paar stappen doen: een datasource neerzetten, deze verbinden met TableMedewerk, en vervolgens laten gebruiken als MasterSource door TableBieb, om tenslotte via de Field Link dialoog de KeyFields te bepalen (Medewerker met Medewerker). Gelukkig kan dat in Delphi 5 een stuk makkelijker, zeker als de tabellen reeds in het Data Diagram staan.
Klik op de "master-detail" knop (de tweede van boven) en sleep nu de master tabel (TableMedewerk) naar de detail tabel (TableBieb). Dit roept direkt de Field Link dialoog op, waarin we aan beide kanten het Medewerker veld kunnen opgeven om de master-detail relatie te voltooien. Zonder secundaire index op het Medewerker veld van de Bieb.db tabel was dat overigens niet gelukt, maar dat terzijde.

master-detail tussen medewerk.db en bieb.db

DataSetProvider
De vorige keer pakte we nu een DataSetProvider om de table naar "de buitenwereld" te exporteren. Datzelfde moeten we nu doen, alhoewel we de tabellen niet naar de buitenwereld zullen exporteren. Echter, InternetExpress werkt met XML packages, en daar hebben we een XMLBroker component voor nodig, en die "praat" op zijn beurt weer alleen met DataSetProviders, vandaar.
Zet dus een DataSetProdiver op de web module, en verbindt deze met de TableMedewerk. We hoeven niet meer op de TableBieb te letten, want alle detail records in deze tabel zullen als "nested table" (een zgn. dataset veld) meegeleverd worden met ieder master records uit de TableMedewerk tabel. Een erg nuttige feature van Delphi, die we ook terugvinden in bijvoorbeeld Oracle 8 (daar zag ik het gebruik van dergelijke nested tables voor het eerst).
Als de DataSetProvider er staat wordt het tijd om naar de InternetExpress tab te gaan en een XMLBroker en een MidasPageProducer component op de web module te zetten. De XMLBroker wordt normaal gesproken verbonden met een DataSetProvider via een Remote Server. Echter, we hebben in dit geval geen Remote Server nodig, dus kunnen we direkt de ProviderName invullen.

lokale datasetprovider naar xmlbroker

Voordat we nu op de MidasPageProducer klikken op de Web Page Editor op te starten, wordt het tijd om wat extra (verborgen) InternetExpress componenten te voorschijn te halen. Deze zitten verborgen in de Delphi 5 DEMOS directory, en wel in de Demos\MIDAS\InternetExpress\INetXCustom directory. Hierin vind je een tweetal packages (een run-time en een design-time). Compileer en installeer de "dclinetxcustom.dpk" InternetExpress sample components design-time package. Dit resulteert in een derde component op de InternetExpress tab: de ReconcilePageProducer. Behalve deze zijn er echter nog een behoorlijk aantal componenten toegevoegd die we pas in de Web Page Editor tegenkomen (maar waardoor de output er nog beter uit kan zien dan vorige keer).

MidasPageProducer
Nadat de "InternetExpress Sample Components" package ge nstalleerd is, kunnen we met de rechter muisknop op de MidasPageProducer de Web Page Editor opstarten. Binnen de Web Page Editor kunnen we steeds met de rechtermuisknop de "New Component" dialoog tevoorschijn toveren, met daarin iedere keer (slechts) de componenten die relevant zijn in de huidige context. Dat is dus om te beginnen de DataForm, QueryForm or LayoutGroup, en nu ook de TitleLayoutGroup of TitleDataForm (de laatste twee zitten in de design-time package die we zojusit ge nstalleerd hebben). De TitleDataForm - laten we die maar nemen - is natuurlijk hetzelfde als de normale DataForm, maar heeft een Caption, CaptionAttributes en CaptionPosition extra. Behalve de Caption, moeten de nu nog een aantal componenten op de TitleDataForm zetten, zoals een FieldGroup (voor het master record), een DataGrid (voor de detail records) en twee ImgDataNavigator componenten. De laatste is weer nieuw, en vervangt de "normale" DataNavigator component die we vorige keer gebruikte. De ene ImgDataNavigator moet (middels de XMLComponent property) verbonden worden met de FieldGroup, de andere met de DataGrid. Daarnaast moeten we in de ImagePathURL property van de ImgDataNavigator aangeven waar (in welk pad of URL) de images gevonden kunnen worden. Voorbeeldplaatjes zijn te vinden in dezelfde directory waar de dclinetxcustom package stond, maar je kan ook zelf wat plaatjes (laten) maken natuurlijk. Default krijg je per ImgDataNavigator alle ImgButtons die relevant zijn voor de XMLComponent waar de navigator aan verbonden is. Dat wil zeggen dat de ImgDataNavigator die aan de FieldGroup is verbonden bijvoorbeeld niet de "NextPage" en "PrevPage" buttons krijgt, die juist weer wel bij het DataGrid zinvol zijn (ze scrollen namelijk precies zoveel records als er in het grid vertoond worden - de volgende/voriga pagina dus). Als je buttons wilt toevoegen of verwijderen kun je dat uiteraard weer doen met de rechtermuisknop (ik zit tegenwoordig meer op de rechtermuisknop te drukken dan code te schrijven).
Er zijn op dit moment nog twee design-time warnings zichtbaar: FieldGroup1.XMLBroker = nil en DataGrid1.XMLBroker = nil. Zet de XMLBroker property van de FieldGroup op XMKBroker1, zodat de eerste warning weg is. Het effect is tevens dat we een groep van vijf velden zien (Medewerker, Voornaam, Achternaam, het calculated field Naam en een vijfde "status" veld met als naam *). Omdat ik eigenlijk alleen maar de Naam wil zien, klik ik weer eens met de rechtermuisknop op de FieldGroup, en voeg ik alleen het veld Naam toe.

web editor (halverwege)

Hetzelfde kunnen we doen met de DataGrid. Echter, als we daar XMLBroker1 toekennen aan de XMLBroker property, zien we natuurlijk weer de velden uit de master tabel (de TableMedewerk), en niet die uit de detail tabel (het dataset field TableBieb). Voor het laatste moeten we de XMLDataSetField property van de DataGrid nog op de waarde TableBieb zetten. Helaas gaat dat niet automatisch door de combolist open te klappen voor de XMLDataSetField property. Dit geeft namelijk de vreemde foutmelding "Missing data provider or data packet".

xmldatasetfield foutmelding

De oplossing voor dit probleem is gelukkig eenvoudig: we moeten de letterlijke naam TableBieb intikken (waarbij we na iedere toetsaanslag de foutmelding van hierboven weer om onze oren krijgen en moeten wegklikken). Na de volledige naam ingetikt te hebben, drukken we op en zien plotsklaps de juiste tabel informatie (voor TableBieb) in de DataGrid tevoorschijn komen (zie volgende screenshot).
Dit is duidelijk een bug in de XMLDataSetField property editor van de DateGrid, die hopelijk in een Update Pack voor Delphi 5 verholpen zal worden.

web editor: master-detail relatie

Merk op dat ik alleen nog maar de browser buttons heb overgelaten. Het betreft hier dan ook slechts een overzicht dat bekeken kan worden (en niet gewijzigd). Voor het wijzigen heb ik een andere (user) interface in gedachten. Omdat de velden hierboven dus niet gewijzigd zouden mogen worden, moeten we ze eigenlijk expliciet read-only maken. Dat is mogelijk, door de velden van de FieldGroup en die van de DataGrid te selecteren en de ReadOnly property op True te zetten. Het resultaat is niet te zien tijdens design-time, maar tijdens run-time zul je zien dat de tekst in de editboxen inderdaad niet meer te wijzigen is (waarbij er een subtiel verschil optreedt tussen Internet Explorer en Netscape Navigator, maar dat is gewoon een browser verschil).

Opmaak
Het interface dat we net zagen zullen we gebruiken om per medewerker te zien welke boeken hij/zij uit de bibliotheek heeft uitgeleend. Om het interface nog een beetje mooier te maken kunnen we de Captions van zowel de velden van de FieldGroup als die van de DataGrid nog van styles of speciale HTML-codes voorzien (ik begin de titelnaam dus bijvoorbeeld met om te zorgen dat deze ook in een tabel met het juiste font wordt afgebeeld).
Een andere plek om de resulterende webpagina al bij voorbaat aan te passen (lees: mooier te maken) is de HTMLDoc of HTMLFile property van de MidasPageProducer. Hierin bevindt zich het HTML template dat tot de webpagina zal leiden. In dit template kunnen we onze eigen achtergrondkleur, font types, etc. opgeven, zodat het resultaat er helemaal volgens onze huisstijl of smaak uit zal komen te zien:

  <HTML>
  <HEAD>
  <TITLE>Delphi 5 InternetExpress (2)</TITLE>
  </HEAD>
  <BODY BGCOLOR="FFFFCC">
  <FONT FACE="Comic Sans MS"SIZE=4><B>TAS-AT Bibliotheek</B></FONT><BR>
  <HR>
  <FONT FACE="Verdana"SIZE=2">
  <#INCLUDES><#STYLES><#WARNINGS><#FORMS><#SCRIPT>
  <HR>
  <FONT SIZE=1>This webpage © 2000 by Bob Swart (aka Dr.Bob - www.drbob42.com)</FONT>
  </BODY>
  </HTML>
De vijf #-tags zullen door de MidasPageProducer worden vervangen door de specifieke inhoud van InternetExpress (de verwijzingen naar de JavaScripts, de HTML styles, de design-time warnings, de HTML Forms en tenslotte de scripting code). We kunnen deze vijf ook vervangen door een #-tag namelijk <#BODYELEMENTS>.

Deployment
Als laatste moeten we weer zorgen dat de Web Module ook daadwerkelijk de MidasPageProducer zal gebruiken. Dit doen we door een Action aan te maken (met Default property op True), en die als Producer property de MidasPageProducer te geven. Daarna moeten we zorgen dat de JavaScript routines die nodig zijn om de XML data te parsen gevonden kunnen worden door de web server applicatie., door ze naar de web server te kopi ren, en deze locatie als URL op te geven in de IncludePathURL property van de MidasPageProducer. Merk op dat we nog steeds maar een single-tier web server toepassing aan het schrijven zijn: er is geen MIDAS server meer bij betrokken (de ISAPI DLL is in feite zijn eigen "data provider").
Vorige keer zagen we dat we met behulp van Internet Explorer 4 of hoger, of Netscape Communicator 4 of hoger het resultaat konden zien, maar dat kan ook met de nieuwe versie van IntraBob v5.0 die als freeware op mijn website te downloaden is. IntraBob is gebasseerd op de Internet Explorer control, dus je moet (helaas) ook al Internet Explorer versie 4 of hoger op je machine hebben, anders doet IntraBob het niet.

IntraBob v5.0
Wat is dat zo bijzonder aan IntraBob? Welnu, het bevat de mogelijkheid om als "host application" op te treden voor een InternetExpress (of normale WebBroker) toepassing mits deze als ISAPI DLL begonnen is (voor een CGI executable is het niet mogelijk om een "host application" op te geven, vandaar). De bediening is heel eenvoudig: specificeer IntraBob als Host Application in de "Run | Parameters" dialoog. Hierna kun je binnen de ISAPI DLL breakpoints zetten waar je maar wilt, omdat de DLL vanuit de Delphi IDE kan worden uitgevoerd (en dus ook vanuit de IDE gedebugged zal worden).
Zodra we op Run klikken wordt IntraBob opgestart als "host application", en wordt een lokaal HTML bestand geladen. In dit HTML bestand moet een CGI Form staan dat we kunnen gebruiken op de ISAPI DLL aan te roepen (en eventueel van input te voorzien). In het huidige geval is het voldoende om de ACTION en een button te hebben, waardoor het HTML bestand erg klein is:

  <HTML>
  <BODY>
  <FORM ACTION="http://www.drbob42.com/cgi-bin/sdgn.dll" METHOD="POST">
  <INPUT TYPE="SUBMIT" VALUE="Submit">
  </FORM>
  </BODY>
  </HTML>
We kunnen in de Options pagina van IntraBob opgeven welke lokale ISAPI DLL geladen zal moeten worden (in plaats van de opgegeven remote SDGN.DLL). Dit zorgt ervoor dat we geen web server nodig hebben, maar zelfs op een lokale machine een ISAPI DLL kunnen debuggen met IntraBob. Als alles goed gaat, zal de SDGN.DLL InternetExpress toepassing in aktie komen, die op mijn machine de volgende output genereert:

internetexpress output in intrabob v5.0

We kunnen in de browser zowel door de verzameling medewerkers heen lopen (de master tabel) als per medewerker de lijst met uitgeleende boeken zien (de detail records). Merk op dat de naam van de medewerker grijs is. Dit is een calculated en dus read-only veld. Voor de boektitel en uitleendatum ben ik vergeten om de ReadOnly property op True te zetten, en deze zijn dus wijzigbaar. Het is echter niet mogelijk om de waarden te wijzigen en ook echt terug te sturen naar de database (op de web server). Hoe we dat doen, en ook met meerdere gebruikers tegelijk boeken kunnen uitlenen zullen we de volgende keer zien, in de derde aflevering over Delphi 5 en InternetExpress.

Meer Informatie
Mocht iemand nog vragen, opmerkingen of suggesties hebben, dan hoor ik die het liefst via .


Dit artikel is eerder verschenen in SDGN Magazine #57 - december 1999

This webpage © 1999-2006 by webmaster drs. Robert E. Swart (aka - www.drbob42.com). All Rights Reserved.