Bob Swart (aka Dr.Bob)
Delphi 6 Enterprise WebSnap

WebSnap is een nieuwe verzameling wizards en componenten waarmee je met Delphi 6 Enterprise websites kunt bouwen. Had Delphi daar niet al iets voor? Jawel, dat was de WebBroker Technology die in Delphi 3 Client/Server begon en langzaam naar de Professional versie verhuisde (alhoewel het bij Kylix om de een-of-andere reden toch weer eerst in de dure versie moet komen). In Delphi 5 was WebBroker al een keer uitgebreid met InternetExpress, om door middel van een XMLBroker de koppeling met MIDAS applicatie servers mogelijk te maken. We zagen toen ook een nieuwe manier van "ontwerpen", door de zgn. Web Page Editor, die je alleen maar liet kiezen uit componenten die op dat moment "relevant" waren. Niet echt heel hoogstaand, en zeker iets om aan te wennen. Toch heb ik veel goede ervaringen met InternetExpress, en ook WebBroker toepassingen heb ik veel gebouwd in de afgelopen jaren. Ik keek dan ook met een kleine beetje argwaan naar de nieuwe vreemde eend in de bijt: WebSnap.
Het belangrijkste doel van WebSnap is om een omgeving te scheppen waarin Delphi ontwikkelaars konden samenwerken met de website designers (die waarschijnlijk geen verstand hebben van Delphi of Pascal, maar wel van JavaScript, en die tools als DreamWeaver of FrontPage gebruiker). Om dit te realiseren is Active Scripting toegevoegd aan Delphi (ook wel server-side scripting genaamd), samen met zogenaamde Script-enabled componenten. De Delphi ontwikkelaar kan de bouwstenen opleveren (en een rudimentaire output genereren), terwijl de JavaScript ontwerper er verder mee kan gaan om er iets echt moois van te maken (met behulp van de WebSnap Design Sufaces zoals de Preview, HTML Result, HTML Script, XML en XSL tabs die we straks nog zullen zien). Het goede nieuws (voor de Delphi ontwikkelaars) is dat WebSnap gebruik maakt van dezelfde componenten die we al jaren gewend waren uit de WebBroker en InternetExpress wereld. We werken nog steeds met PageProducers en Web Modules. Alhoewel ze deze keer behalve Dispatchers ook Adapters gebruiken (daarover straks meer). Het grote nadeel is echter dat het wel mogelijk is om vanuit een nieuwe WebSnap toepassing de "oude" technieken te gebruiken (dus je bestaande WebBroker en InternetExpress kennis blijft bruikbaar), maar het is niet mogelijk om een "oude" (legacy) WebBroker/InternetExpress toepassing zomaar om te zetten naar WebSnap of te laten profiteren van de WebSnap componenten. Het is als een DVD speler die ook muziek CD's kan afspelen, maar je kan geen DVD in je autoradio/cd-speler afspelen (en da's maar goed ook denk ik).

Documentatie
Alhoewel WebSnap iets geheel nieuws is in Delphi 6, laat de documentatie nogal te wensen over (er zijn inmiddels wat artikelen op de Borland Community website, en er is een nieuwe versie van het WebSnap hoofdstuk uit de Delphi 6 manual). Er zijn een heleboel verzachtende omstandigheden (het documentatieteam heeft erg veel ziektes en zelfs een sterfgeval ervaren), maar het resultaat is wel dat er vrij weinig op papier staat betreffende de WebSnap tools en de 17 componenten op de WebSnap tab:

Van links naar rechts vinden we de volgende componenten op de WebSnap tab:

En zoals gezegd zijn ook de TPageProducer, TDataSetPageProducer en TINetXPageProducer (de nieuwe naam voor TMidasPageProducer) gewoon bruikbaar. De TDataSetTableProducer en T(SQL)QueryTableProducer lijken op het eerste gezicht wat minder bruikbaar. Tot slot kun je te allen tijde ook eigen TAdapter of TPageProducer componenten schrijven die in de WebSnap architectuur in te klikken zijn (en ook direct gebruikt kunnen worden in de WebSnap wizards), zoals bijvoorbeeld WML specifieke componenten voor WAP telefoontjes. Maar daar kom ik in een later artikel nog wel een keer op terug.

Demonstratie
De beste manier om te zien wat WebSnap kan, is om het gewoon maar te doen. Toen Delphi 5 in 1999 beschikbaar kwam heb ik meermalen laten zien hoe je met de (toen) nieuwe InternetExpress de beroemde customer-orders combinatie in een webbrowser kan laten zien. Met WebSnap zien we een aantal bekende dingen terug, maar meer nog nieuwe zaken en ontwikkelingen. We starten een WebSnap application door File | New | Other en dan naar de WebSnap tab van de Object Repository te gaan. Daar bevinden zich drie icoontjes: de WebSnap Application, een WebSnap Data Module en een WebSnap Page Module. Van de eerste heb je er maar een nodig, van de tweede meestal ook (tenzij je je gebruik wilt maken van meerdere data modules), en van de derde kun je er zoveel gebruiken als je maar wilt. In principe kun je iedere pagina van de (dynamische) website een eigen Page Module geven.
We beginnen met de WebSnap Application, en krijgen de volgende dialoog (wie Delphi 6 Enterprise heeft kan nu "meespelen" en zijn eigen keuzes invullen, uiteraard):

Ik kies nu even voor een CGI toepassing (die is het makkelijkst te testen en weer uit het geheugen te halen). Voor een productieomgeving zou ik altijd voor een ISAPI/NSAPI of Apache DLL kiezen. Merk op dat we in Delphi 6 nu ook een speciale Web App Debugger executable kunnen kiezen: daarmee krijgen we een COM schil om de web module heen die je kan gebruiken om de web server toepassing in de Delphi IDE te debuggen. Zit ook in Delphi 6 Professional, dus zou wel eens het eind van IntraBob kunnen betekenen, maar da's een ander verhaal.
De Components knop in de "Application Module Components" kan gebruikt worden om aan te geven welke hulp-componenten we nodig verwachten te hebben. Per optie kunnen we uit een drop-down combobox kiezen. Op dit moment zijn de keuzes nog beperkt (alleen de EndUserAdapter kan ook een EndUserSessionAdapter worden), maar zodra je zelf speciale componenten maakt voor WebSnap dan komen die ook hier in de comboboxen te hangen.

Default staan alleen de eerste, derde en vierde checkbox aan; de rest uit. De tweede knop op de WebSnap Aplication Wizard dialoog kan gebruikt worden om de Page Options op te geven. Hierin kunnen we aangeven wat voor soort PageProducer gebruikt moet worden (voorlopig de keuze uit de PageProducer, DataSetPageproducer, AdapterPageProducer, INetXPageProducer of de XSLPageProducer). Ook kan je hier de script-engine kiezen (default op JScript) en aangeven wat voor soort template gemaakt moet worden. Het enige dat ik echter invul is de naam van de pagina (Home) en de titel (SDGN Magazine).

De optie Published zorgt ervoor dat we de pagina ook echt zien. en de Login optie zou ervoor zorgen dat de bezoekers moeten inloggen voordat ze de pagina mogen zien (de Home pagina wil je altijd Published hebben, en over het algemeen zonder directe Login -die volgt indien nodig pas bij de overige pagina's).
Als we dan uiteindelijk op de OK knop van de WebSnap Application Wizard dialoog klikken krijgen we de WebSnap web module, met daarin een vijftal componenten. De PageProducer, de WebAppComponents (met properties die wijzen naar de rest), en de drie componenten die we in de Web App Components dialoog hadden geselecteerd.

Ik bewaar mijn project in WebSnapCGI.dpr, en de web module in WebSnapWebMod.pas.

WebSnapDataMod
Nu is het tijd om een (of meerdere) data module toe te voegen aan de WebSnap toepassing. Ook hiervoor moeten we in de WebSnap pagina van de Object Repository zijn, en dan voor de WebSnap Data Module kiezen:

Behalve de keuze "On Demand" kunnen we de data module ook "Always" aanwezig laten zijn. Voor de CGI toepassing die ik nu aan het bouwen ben maakt het niet zoveel uit (die wordt toch na iedere request weer uit de lucht gehaald), maar voor een ISAPI of Apache web toepassing is deze optie wel van belang. Net als de Caching optie, die behalve de waarde "Cache Instance" ook "Destroy Instance" kan krijgen. Let erop dat bij de keuze "Cache Instance" de data module niet opnieuw in de OnCreate event handler komt, en zijn oude "status" blijft houden (dus queries en/of tabellen blijven gepositioneerd op de plek waar de vorige gebruiker deze heeft achtergelaten). Nadat ik op OK heb geklikt, bewaar ik de nieuwe data module in bestand WebSnapDataMod.pas. Ik plaats er tevens twee ClientDataSets op (cdsCustomer en cdsOrders), en laat de ene verwijzen naar customer.xml en de tweede naar orders.xml (de aloude DBDEMOS tabellen zijn nu ook in binaire .cds of MyBase .xml formaat beschikbaar). Een master-detail relatie is zo gemaakt met behulp van een DataSource component dat naar cdsCustomers wijst, en als MasterSource door cdsOrders wordt gebruikt. De MasterFields property kan vervolgens gebruikt worden om de CustNo velden aan elkaar te koppelen:

Met WebSnap kunnen we stateless web server toepassingen maken. Dat wil dus zeggen dat de WebSnap web server toepassing niet voor alle clients (browers) gaat bijhouden bij welk record ze zijn. Dat moet de client zelf doen, en ook zelf doorgeven aan de WebSnap server. Voor de cdsCustomer en cdsOrders datasets moeten we hiervoor duidelijk aangeven wat de key velden zijn. Selecteer eerst cdsCustomer, klik met de rechter muisknop om de Fields Editor te krijgen, en klik weer met de rechter muisknop om Add all Fields te kunnen kiezen. Nu zijn alle velden van de customer tabel aanwezig. Het CustNo veld is in dit geval de unieke key. Selecteer dit veld, en ga naar de Object Inspector waar je van de ProviderFlags property de sub-property pfInKey op True moet zetten. Doe hetzelfde voor de cdsOrders dataset, maar kies daar voor het OrderNo veld. De WebSnap toepassing zal nu altijd de juiste velden meegeven om de records waar we mee willen werken uniek te kunnen identificeren.
We moeten nu nog een ding doen, en dat betreft het plaatsen van twee Adapter componenten (beide een DataSetAdapter in dit geval - het derde component van de WebSnap tab) om de velden uit zowel de Customer als de Orders dataset beschikbaar te maken (als AdapterFields) voor de rest van de WebSnap toepassing. Ondanks het feit dat er al een master-detail relatie gebruikt wordt, zullen we twee DataSetAdapters moeten gebruiken: eentje voor cdsCustomer (CustomerAdapter) en eentje voor cdsOrders (OrdersAdapter). De master-detail relatie tussen de twee Adapters kunnen we vervolgens weer aangeven door CustomerAdapter te selecteren als MasterAdapter voor de OrdersAdapter.

In de Object Treeview kunnen we de beide DataSetAdapter componenten openklappen, en dan zien we de Actions en Fields subproperties. Door met de rechter muisknop op Actions te klikken kunnen we uit alle 11 mogelijke actions juist alleen diegene toevoegen die we toe willen staan op de velden uit de Customer en Orders datasets.

Stel dat je bijvoorbeeld nu al weet dat je de data nooit wilt laten editen, dan is dit het moment om te zorgen dat de EditRow action niet wordt toegevoegd aan de lijst. In dit geval wil ik alles toestaan, dus ik had net zo goed "Add All Actions" kunnen kiezen.
We zijn nu klaar met de data module, die ik onder de naam WebSnapDataMod.pas bewaar.

Eerste WebSnapPageMod
Nu we de WebSnap toepassing en data module hebben gemaakt wordt het tijd voor enkele WebSnap Page Modules: de hulpmiddelen om de daadwerkelijke (dynamische) webpagina's te genereren. Wederom naar de WebSnap tab van de Object Repository om deze keer voor WebSnap Page Module te kiezen: Deze dialoog lijkt erg op die van de nieuwe WebSnap Application, maar heeft geen speciale Component en Page Options knoppen meer. We kunnen wel aangeven wat voor soort PageProducer component gebruikt moet worden voor deze pagina. In de screenshot is te zien dat ik voor een AdapterPageProducer heb gekozen (die kan automatisch de scripting code genereren voor een Adapter), zodat ik me dus zelf niet bezig hoef te houden met het schrijven van het active script (dat zal ik een andere keer laten zien). Wie dat nu wel wil, had ook een andere PageProducer kunnen kiezen. Daarnaast heb ik ook de naam (Customers) en Titel (Customers Overview) van de pagina opgegeven.

Als je op OK-klikt krijg je een nieuwe Page Module, met daarin al automatisch een AdapterPageProducer component (zoals we aangaven). Voor we verder gaan wil ik deze nieuwe unit eerste bewaren als WebSnapPageMod01.pas (er komt straks ook nog een tweede).
Verder zullen we gebruik willen maken van de CustomerAdapter in de WebSnap Data Module, dus moeten we de unit WebSnapDataMod aan de uses clause toevoegen (bijvoorbeeld met File | Use Unit). Als dat geregeld is, kunnen we de AdapterPageProducer in de Object Treeview selecteren en openklappen. De property WebPageItems verschijnt (wat bij sommige een herinnering kan oproepen aan InternetExpress). Vanaf hier kunnen we nieuwe componenten toevoegen door met de rechter muisknop op een component in de Object Treeview te klikken en voor "New Component" te kiezen. Dit geldt voor de WebPageItems en alles wat daar straks onder komt. We beginnen met een AdapterForm direct onder de WebPageItems, en op dit AdapterForm een AdapterGrid. Hier wil ik een (verkort) overzicht geven van de Customers - zonder de orders. Wie in de tussentijd een blik op de Code Editor heeft geworpen zal het niet ontgaan zijn dat er in plaats van een Code tab (en een Diagram tab die ook nieuw is in Delphi 6) er een aantal extra tabs te zien zijn, zoals WebPageMod01.pas, WebPageMod01.html, HTML Result, HTML Script en Preview. De eerste twee tabs zouden nog Unit1.pas en Unit1.html kunnen heten (de namen van de unit voordat ze bewaard werden). Da's een klein foutje in Delphi 6, maar je kunt je juiste namen in de tabs krijgen door even naar een ander source bestand te gaan en weer terug naar de WebPageMod01 (zodat de tabs hun nieuwe - juiste - namen krijgen). De inhoud van de eerste twee tabs is overigens gewoon te editen, de derde en vierde zijn echter read-only en laten zien hoe de HTML en scripting die gegenereerd worden eruit komen te zien. De vijfde tab geeft een echte preview alsof we een browser gebruiken. Dit is tevens de pagina die op dit moment heel duidelijk een design-time warning geeft dat de Adapter property van de AdapterGrid1 nog nil is. Dat kunnen we oplossen door terug te gaan naar de Object Treeview, de AdapterGrid1 te selecteren en in de Adapter property de CustomerAdapter van WebSnapDataMod te kiezen (die kunnen we pas kiezen als WebSnapDataMod ook daadwerkelijk in de uses clause staat). Als je vervolgens ook tijdens design-time echte data wilt zien, moet je er ook voor zorgen dat cdsCustomer geopend is (de Active property op True). We zien nu alle 13 velden van cdsCustomer, en dat vind ik persoonlijk wat veel voor een gewone overview waarin we alleen maar de juiste customer willen selecteren om vervolgens pas alle details te zien (inclusief bijbehorende orders). Kortom: ik wil velden verwijderen. Daarvoor moet ik niet terug naar de WebSnap Data Module (voor wie ze uit de cdsCustomer wil verwijderen), want dan heb ik ze straks bij de detail view ook niet meer. Nee, ik moet gewoon de AdapterGrid weer selecteren, met de rechter muisknop klikken en kiezen voor "Add All Columns". Als ik ze eenmaal alle 13 zie, kan ik er rustig enkele verwijderen (zoals het adres enzo). Tip: om een Col te verwijderen in de TreeView moet je op de "delete" knop in de TreeView klikken, of met de rechter muisknop op de columnnaam klikken en dan via Edit de keuze Delete maken. De delete toets zelf heeft bij mij geen effect, helaas. Uiteindelijk hou ik maar vier velden over: ColCustNo, ColCompany, ColCity en ColCountry. Ik wil echter nog wel een vijfde column toevoegen in het AdapterGrid, waar ik dan een speciale knop in wil plaatsen die naar de "detail" view gaat. Dit doe ik door weer met de rechter muisknop op de AdapterGrid te klikken en voor New Component te kiezen. De keuze valt op een AdapterCommandColumn, die default met alle mogelijke command knoppen begint (DeleteRow, FirstRow, PrevRow, NextRow, LastRow, EditRow, BrowseRow, NewRow en RefreshRow). Ik wil er eigenlijk maar eentje zien, dus klik ik weer met de rechter muisknop, deze keer op de AdapterCommandColumn en kies weer New Component, en kies deze keer één enkele AdapterActionButton. Zodra ik deze keuze heb gemaakt komt er weer een design-time warning in beeld: de AdapterActionButton heeft een lege Action! Door voor de EditRow action te kiezen lossen we dat op. Ik zou dan wel de caption veranderen in "Details" of "Edit Details" of zoiets (dat staat beter dan "EditRow").

Tot slot kunnen we via de PageName property aangeven naar welke pagina er gesprongen moet worden als de gebruiker daadwerkelijk op de "Details" knop drukt. Vul hier CustomerOrders in. Die bestaat nog niet, maar die gaan we dan ook meteen maken. Ik had eigenlijk wel verwacht dat ik uit een lijst met bestaande PageNames zou kunnen kiezen (maar misschien is dat iets voor een custom property editor die ik hiervoor kan schrijven).

Nog een WebSnapPageMod
Tijd voor de tweede WebSnap Page Module. Gebruik weer dezelfde wizard uit de WebSnap tab van de Object Repository, en kies wederom voor een AdapterPageProducer. Als naam moeten we dus CustomerOrders opgeven, als titel heb ik Customer Orders Detail View gebruikt.

Merk op dat ik de Published checkbox uit heb gezet, zodat we niet direct vanuit de Home pagine op deze pagina kunnen komen!
Ook deze Page Module (die ik bewaar in WebSnapPageMod02.pas) heeft de WebSnapDataMod nodig in de uses clause om bij de CustomerAdapter en OrdersAdapter te kunnen komen. En wederom kunnen we een min-of-meer visueel ontwerp maken van de webpage door in de Treeview verschillende componenten toe te voegen aan de WebPageItems property. Ik start weer met een AdapterForm, maar deze keer stop ik daar een AdapterCommandGroup, AdapterFieldGroup en AdapterGrid op.

De DisplayComponent property van de AdapterCommandGroup koppel ik aan de AdapterFieldGroup. De Adapter property van de AdapterFieldGroup koppel ik aan de CustomerAdapter van de WebSnap Data Module, en de Adapter property van de AdapterGrid koppel ik aan de OrdersAdapter. Deze keer wil ik in de detail view wel alle velden zien, en ook alle mogelijke akties toestaan (zoals het editen of verwijderen van records). Als laatste speeltje kun je tijdens design-time vast een voorproefje krijgen van de verschillende manieren waarop de data gerepresenteerd kan worden. Kijk maar eens naar de AdapterMode property van de AdapterFieldGroup component. De mogelijke waarden zijn Browse, Edit, Insert en Query. Omdat wij via de EditRow in deze pagina zullen komen, zou je alvast kunnen vermoeden wat de AdapterMode zal zijn als we voor het eerst op de pagina komen.

Aktie!
We zijn klaar. En hebben niet één regel code geschreven! Alleen maar klikken en koppelen eigenlijk. Even compileren en draaien maar. Of toch niet? We moeten eerst nog even zorgen dat we niet alleen de WebSnapCGI.exe maar ook de html bestanden voor de Home, Customer en CustomerOrders pagina's goed gedeployed worden. Zonder gebruik te willen maken van de LocateFileService component moet je die html bestanden naast de executable in de scripts directory neer moeten zetten. De eerste pagina is nog niet zo spannend (alleen maar SDGN Magazine als titel en twee links: naar Customers Overview en Customer Orders Detail View), maar de tweede pagina geeft een mooie Customers Overview:

Als we op een van de Details knoppen drukken krijgen we inderdaad de details van die betreffende rij te zien. Alleen dan wel in Edit mode, want we hebben voor de EditRow aktie gekozen (we hadden ook voor de BrowseRow aktie kunnen kiezen natuurlijk):

De pagina is ook terug te zetten in de Browse mode door op de BrowseRow knop te drukken. En natuurlijk kun je de nodige velden verwijderen als ze in de weg staan. En de titels of captions wijzigen (want die veldnamen zijn wel erg lelijk zo). Waar het om gaat is dat we met behulp van WebSnap en Adapter componenten in een paar minuten een webtoepassing in elkaar hebben gedraaid. En dat is nog veel en veel meer mogelijk is, dus ik kom er de volgende keer graag nog op terug.

Meer Informatie
Mocht iemand nog vragen, opmerkingen of suggesties hebben, dan hoor ik die het liefst via . Wie meer wil weten over web server toepassingen met WebSnap moet zeker eens overwegen om zich in te schrijven voor een van mijn Delphi Clinics.


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