Delphi - TWebFormGenerator komponens

Internet áruház 3. rész

forráskód letöltése
Az internetes áruházunk fejlesztése során most egy olyan komponenst készítünk, melynek segítségével könnyedén előállíthatunk olyan HTML formátumú adatbeviteli ablakokat, melyeket tetszőleges web lapon felhasználhatunk.

Ehhez a komponenshez tetszőleges típusú és szerkezetű adatbázis kapcsolható, ami alapján a komponens automatikusan elkészíti a beviteli ablakhoz szükséges objektumokat. Ezek után persze még lehetőségünk van egyedi beállítások elvégzésére is, minden egyes adatbeviteli lap elkészítésekor. >A mellékelt példaprogram megnyitása előtt a WebFormGenerator.pas-ban lévő komponenst telepítenie kell a Delphi alá. Továbbá kérjük hozza létre az ASWEBSHOP03 alias-t. Ez mutasson a mellékelt példaprogram Data könyvtárára, az adatbázis elérés végett.
A lefordított WS.EXE-t helyezze a web szerver scripts könyvtárába, ahonnan az futtatható.
A HTM könyvtárban lévő Index.htm állományt pedig helyezze el a web szerveren úgy, hogy az egy böngészőn keresztül elérhető legyen. Például: C:\Inetpub\wwwroot\webshop. Ekkor a böngészőbe a www.animare.hu/webshop/ cím beírásakor meg kell hogy jelenjen az iménti web lap. Persze a web szerver neve mindenkinél más és más lesz.

A jelenlegi cikk elolvasása előtt javasoljuk a sorozat előző részeinek a megismerését, mivel a mostani ismeretek csupán kiegészítői az előzőeknek, így azok ismerete nélkül e konkrét cikk tartalma sem érthető meg teljes egészében.

A komponens megvalósításánál a cél az lesz, hogy a HTML kódban ismert <FORM> elemhez szükséges kódot generálja. Nem célja az adatbeviteli ablak kinézetének befolyásolása. Erre majd egy másik komponens fog szolgálni, felhasználva a TWebFormGenerator által szolgáltatott HTML kódot. Ebből kifolyólag a készítendő web lap még mindig nem nevezhető szépnek, de ebben a fejezetben is a működőképességen van a hangsúly.

A cikk végére a komponens már eljut a működőképesség határáig, de nem valósít meg minden lehetőséget, melyre a későbbiekben még szükségünk lesz. Ezt majd jövőbeni cikkekben hozzátesszük a komponenshez ott, amikor tényleges szükségünk lesz az adott funkcióra.

Egy kis kitérővel nézzük meg, hogy milyen HTML kódra van szükség egy web lapon történő adatbevitelhez.
<form action=/scripts/ws.exe/addnewproduct method=get>
   <input name=NAME type=text size=64 maxlength=64><br>
   <input name=PRICE type=text size=10><br>
   <input name=TAX type=text size=10><br>
   <input name=POSTAGE type=text size=10><br>
   <input name=BTOITJ type=text size=10 maxlength=10><br>
   <input name=ME type=text size=5 maxlength=5><br> 
   <input name=Submit value=Ok type=submit><br>
</form>
Látható tehát, hogy a legalapvetőbb szükségletekhez is három elkülöníthető részre lesz szükség a kódolásnál. Az első a kezdeti <form> utasítás a szükséges paramétereivel. Ezután következhetnek az adatbeviteli objektumok, <input> kulcsszóval. Ezek száma tetszőleges lehet, attól függően, hogy hány beviteli ablakra van szükségünk. Végül lezárásként a </form> parancsra van szükség.

A komponens jelenleg csak kétféle adatbeviteli objektumot képes kezelni. Ebből az egyik az egysoros, szöveges adatbeviteli ablak, mely a Delphi-ből ismert TEdit-nek felel meg. Ekkor az <input>-nál a type=text-el adhatjuk meg a típust.

A másik pedig egy olyan speciális nyomógomb, mely az adatbeviteli lap tartalmának elküldését teszi lehetővé a szerver programunk felé. Ekkor az <input>-nál a type=submit-el adhatjuk meg a típust.

Minden esetben a NAME paraméter adja meg az objektum nevét, míg a VALUE az értékét. A szövegbeviteli ablaknál még használhatjuk a SIZE paramétert a méret megadásához, valamint a MAXLENGTH paramétert, ha korlátozni szeretnénk a bevihető karakterek számát.

Nézzük tehát a komponenst. Szükségünk lesz egy adatbázisra, melyhez a beviteli ablakot készítjük. Ehhez létrehozunk egy DataSet nevű TTable típusú property-t.

A <form>-nak két paraméterét használjuk most fel. Az egyik a küldés típusát befolyásolja. Ezt kell a METHOD paraméterben megadni és ehhez hozunk létre egy Method nevű property-t, mely két értéket tartalmazhat: fmGet és fmPost néven. A másik szükséges érték amit meg kell adnunk, az annak a programnak a neve, mely az adatokat feldolgozza majd. Ehhez egy Action nevű property-t hozunk létre. Az itt megadott értékek alapján a komponens majd elkészíti a szükséges HTML kódot. Például:
<form action=/scripts/ws.exe/addnewproduct method=get>

A kérdés kód lekérdezéséhez készítünk egy Content nevű függvényt, mely sztring típusban adja vissza a kért HTML kódot. Ehhez viszont egy paramétert kell adnunk a függvénynek, hogy tudja, mi az igényünk. A cikk elején említettük, hogy három nagy részre bontható a HTML kódolás a <form> esetében. Ezért itt most egy olyan paramétert adunk át, mely arra utal, hogy a három rész közül melyik kódjára vagyunk kíváncsiak.

Ennek eldöntéséhez létrehozunk egy új felsorolt típust TWebFormElement néven. Értékei az alábbiak lehetnek:
  • feStart - <form ... kódot adja vissza a Content függvény
  • feItem - <input ... kódot adja vissza a Content függvény
  • feEnd - </form> kódot adja vissza a Content függvény
Tehát amikor a Content függvényt a feStart paraméterrel hívjuk, akkor kell elkészítenünk a <form...> HTML kódot. Ezt egyszerűen megtehetjük az Action és a Method property-k tartalmának ismeretében.
function TWebFormGenerator.Content(FormElement: TWebFormElement;
  Index: integer): string;
...
  case FormElement of
    feStart: begin
      result:='<form';
      if FAction<>'' then begin
        result:=result+' action='+FAction;
      end;
      case FMethod of
        fmGet: begin
          result:=result+' method=get';
        end;
        fmPost: begin
          result:=result+' method=post';
        end;
      end;
      result:=result+'>';
    end;
...
Amikor majd a harmadik részre lesz szükségünk, vagyis amikor a feEnd értékkel hívjuk a Content függvényt, akkor még egyszerűbb lesz a dolgunk, mivel itt nincs más teendő, mint a </form> kódot visszaadni.
...
    feEnd: begin
      result:='</form>';
    end;
...
Nézzük viszont most a középső elemet, a feItem-et, amikor egy <input... elemet kell generálnunk.

Ahhoz, hogy ezt megtehessük további információkra van szükségünk, melyekkel még nem szolgál a komponensünk, így most újabb property-kkel kell kiegészítenünk azt.

Mivel egy-egy beviteli ablakban valószínűleg nem csak egy beviteli mezőnk lesz, így itt most egy olyan megoldásra lesz szükségünk, ahol - mintha csak egy tömbben adnánk meg az adatokat - tetszőleges számú elemet létrehozhatunk.

Ehhez szükségünk lesz a TCollection - TCollectionItem osztály páros felhasználására. Ezek segítségével készítjük el a FormItems nevű TFormItems típusú property-nket, mely egy tömbben egy újabb osztályt képes tárolni, ami megfelel egy-egy beviteli mezőnek az adatlapon.

Nézzük mit is tartalmaz a TFormItems osztály.

Találunk benne egy DataField nevű property-t. Itt kell megadnunk azt az adatbázis mező nevet, melyhez a beviteli ablakot készítjük. Erre a property-re persze csak akkor lesz szükségünk, amikor ténylegesen adatbeviteli ablak kerül létrehozásra. Viszont például egy nyomógomb esetén ezt a property-t nem fogjuk felhasználni.

Kell továbbá egy ItemType property, melyben eldönthetjük, hogy mi legyen az adatbeviteli objektum típusa. Ennek lehetőségeit a későbbi cikkeinkben még bővítjük. Jelenleg csak két típust kezel: a TEXT (TEdit) és SUBMIT (nyomógomb, TButton).

A Name property-ben megadhatunk egy tetszőleges nevet, mely majd azonosítja az objektumot. Adatbázis mezők esetén célszerű a mezőnevet választanunk.

A Value property-ben határozhatjuk meg, hogy a megjelenéskor milyen értéke legyen az adott objektumnak. Adatbeviteli mező esetén itt akár egy alapértelmezett értéket is megadhatunk az új rekordok számára.

A Size property-ben határozható meg az objektum szélessége, míg a MaxLength-nél korlátozhatjuk a bevihető karakterek számát. Itt nulla esetében nincs korlátozás.

Ha a ValueFromDataSet property-t igazra állítjuk, akkor a Value property értéke automatikusan az adatbázis aktuális rekordjának és a DataField-ben megadott mezőjének az értéke lesz. Erre majd a későbbiekben lesz szükségünk, amikor már nem csak új rekordokat viszünk fel, hanem meglévőket módosítanunk is kell.

Hogy ne kelljen az adatbázis minden egyes mezőjét felvennünk manuálisan és beállítgatni ezeket a property-ket, létrehoztunk a komponensen belül egy AddAllFields nevű eljárást, mely ezt megteszi helyettünk. Nekünk csak annyi a dolgunk, hogy a komponensre jobb gombbal kattintunk, majd az Add all database fields menüpontot választjuk. Ekkor a komponens FormItems tömbjéhez hozzáadásra kerül az összes adatbázis mező. Ezek után természetesen tetszőlegesen módosíthatjuk még a property-k értékét, törölhetünk, vagy akár létrehozhatunk újat is.

Ha például egy nyomógombra is szükségünk lenne, akkor ehhez szintén elegendő a komponensen jobb gombbal kattintani és az Add Submit button menüpontot választani. Ekkor automatikusan egy új, Submit típusú gomb kerül a FormItems-be.

Mostantól tehát már adottak a Content függvény <input... kódjához szükséges adatok. Most már csak le kell generálnunk ezt.

Amikor a Content függvénynek a feItem értéket adjuk, akkor kell egy-egy beviteli ablak HTML kódját visszatérési értékként szolgáltatnunk. Ehhez viszont tudnunk kell azt is, hogy a FormItems property tömbjéből melyik elemre van szükség. Ehhez létrehozunk egy második paramétert is a Content függvénynek, mely Index névre fog hallgatni és integer típusú lesz. Amikor majd meghívjuk a Content függvényt, akkor itt adhatjuk meg, hogy melyik elem kódjára vagyunk kíváncsiak.

Most már tényleg minden adat megvan, nézzük hát a HTML kód gyártását.

A visszatérési érték biztos, hogy az <input szöveggel kezdődik, így ezt nyugodtan hozzáadhatjuk. Ezután vizsgáljuk, hogy lett-e érték adva a Name property-nek, ha igen, akkor folytatódhat a sztring: <input name=valami

Hasonló a helyzet a Value property-nél, itt viszont figyelembe kell vennünk a ValueFromDataSet property értékét is.

Ezután elágaztatjuk a programot az objektum típusa szerint (ItemType property). Megadjuk a HTML kódban is a szükséges típusú paramétert és ha kell, akkor a Size és MaxLength értékeket is.
...
    feItem: begin
      result:='<input';
      with FFormItems[Index] do begin
        if Name<>'' then begin
          result:=result+' name='+Name;
        end;

        if ValueFromDataSet then begin
          if Assigned(FDataSet) and (DataField<>'') then begin
            result:=result+' value='+FDataSet.FieldByName
                 (DataField).AsString;
          end;
        end else begin
          if Value<>'' then begin
            result:=result+' value='+Value;
          end;
        end;

        result:=result+' type=';
        case ItemType of
          itText: begin
            result:=result+'text';
            if Size<>0 then begin
              result:=result+' size='+IntToStr(Size);
            end;
            if MaxLength<>0 then begin
              result:=result+' maxlength='+IntToStr(MaxLength);
            end;
          end;
          itSubmit: begin
            result:=result+'submit';
          end;
        end;
      end;
      result:=result+'>';
    end;
...
Mivel a komponens jelenleg még nem valósít meg minden HTML-ben használható funkciót, így létrehozunk egy OnCustomTag nevű eseményt is, melynek segítségével minden egyes Content függvény hívásakor lehetőségünk van egy tetszőleges szöveg, újabb kód beszúrására.

Amikor meghívjuk a Content függvényt, akkor rögtön aktivizálva lesz ez az esemény, ahol tetszőleges kódot szúrhatunk be az eredménybe. Ezt felhasználva például elérhető, hogy a beviteli ablakok közül csupán az elsőnek legyen piros szövege, a többinek ne.
function TWebFormGenerator.Content(FormElement: TWebFormElement;
   Index: integer): string;
var
  tag: string;
begin
  tag:='';
  if Assigned(OnCustomTag) then begin
    OnCustomTag(FormElement, Index, tag);
  end;
...
A komponens ezzel kész (átmenetileg), nézzük most a felhasználási lehetőségét.

Amikor a mellékelt web lapon az Új termék nevű linkre kattintunk, akkor kell egy olyan web lapot generálnunk, melyben megtalálható egy <form> ... </form> HTML kódrészlet is, ami az adatbevitelt lehetővé teszi. Tehát a linkre kattintva meghívásra kerül a programunk a newproduct paraméterrel. Ehhez létrehoztunk egy Action-t, melynek OnAction eseményekor kell a generálást végrehajtani.
procedure TWebModule1.WebModule1WebActionItem2Action(
  Sender: TObject; Request: TWebRequest; Response: 
  TWebResponse; var Handled: Boolean);
begin
  Response.Content:=PageProducer1.Content;
end;
Ehhez egy TPageProducer komponenst használunk (amíg nincs jobb). Itt a HTMLDoc property-n keresztül megadtuk egy nagyon egyszerű web lap HTML kódját, melyből kihagytuk a <form> ... </form> részt, mivel most már ezt az új komponensünknek kell majd elkészítenie.

Hogy hova is kell ezt beilleszteni, azt úgy tudjuk szabályozni, hogy elhelyezünk ebbe a HTML kódba egy címkét, melyet majd a feldolgozás közben kicserélhetünk egy tetszőleges szövegre. Ez a címke most a <#form> lesz.

Tehát amikor a PageProducer1 komponensnek a Content függvényét hívjuk, akkor az végignézi a számára megadott HTML kódot és ha címkét talál benne, akkor fogja az OnHTMLTag eseményt generálni.

Ez lesz az a pont a programban, amikor a komponensünket segítségül hívva elkészíthetjük az adatbeviteli lap kódját.

Itt három lépésre van szükségünk. Először le kell kérnünk a <form.. sort a komponensünk Content függvényének segítségével és a feStart paraméterrel. Ezután egy ciklus segítségével annyiszor hívjuk meg a Content függvényt a feItem paraméterrel, ahány eleme van a komponens FormItems tömbjének. Végül már csak a </form>-ra van szükségünk lezárásként, melyet egy ismételt Content függvény hívással tudunk lekérni.
procedure TWebModule1.PageProducer1HTMLTag(Sender: TObject; 
  Tag: TTag; const TagString: String; TagParams: TStrings; 
   var ReplaceText: String);
var
  i: integer;
begin
  if TagString='form' then begin
    ReplaceText:=WebFormGenerator1.Content(feStart);
    for i:=0 to WebFormGenerator1.FormItems.Count-1 do begin
      ReplaceText:=ReplaceText+WebFormGenerator1.FormItems[i].
        DataField+' '+WebFormGenerator1.Content(feItem, i)+'<br>';
    end;
    ReplaceText:=ReplaceText+WebFormGenerator1.Content(feEnd);
  end;
end;
A TWebFormGenerator komponens még jelentős fejlesztés előtt áll. A mostani csupán a kezdet volt, de már most is jól látható, hogy segítségével milyen egyszerűen készíthetünk adatbeviteli ablakokat web lapra egy tetszőleges szerkezetű adatbázishoz.

Cikksorozatunk következő részében végre egy kicsit a külalakra is szentelünk időt és létrehozunk egy olyan komponenst, mely ebben nagy segítségünkre lesz majd.

Internet áruház cikksorozat