TutWebAppServer Lesson 4 - Building tables

From Overbyte
Jump to navigation Jump to search

During this lesson, you will learn how to build a table with a variable number of rows containing dynamic data. In a typical application, a table is used to show the result of a database query (for example a list of items matching a given search criteria) or to build a list of items to fill a HTML combobox or anything else repetitive.

In this lesson, to avoid database stuff we have not yet learned, we will do something simple and yet useful: We will create a password generator. The user will be presented a form where he can enter the number of passwords he wants. The program will generated those passwords and present them in a nice HTML table.

Since we already learned how to handle HTML forms to enter data, I will quickly pass over those steps to come directly tho this lesson's objective: generate a table.

Steps

  1. Create the entry form to enter the number of passwords the user want to get. Save the form to c:\icstutorial\templates\GenerateTable.html. Create the code to handle the form and to get the value back. Save the code into a new unit named OverbyteIcsTutWebAppServerGenerateTable.pas. Name the two classes TUrlHandlerGenerateTable and TUrlHandlerDoGenerateTable. Map the two classes to the URL GenerateTable.html and DoGenerateTable.html. For detailed steps, please see lesson 2.
    The HTML form include the following code:
    <form action="DoGenerateTable.html"
    method="GET"
    enctype="application/x-www-form-urlencoded">
    Enter the number of passwords you want
    <input type="TEXT" name="PasswordCount" size="3">
    <input type="SUBMIT" value="Generate">
    </form>

    The unfinished new Delphi unit looks like:
    unit OverbyteIcsTutWebAppServerGenerateTable;

    interface

    uses
    SysUtils, OverbyteIcsHttpAppServer, OverbyteIcsHttpSrv;

    type
    TUrlHandlerGenerateTable = class(TUrlHandler)
    public
    procedure Execute; override;
    end;

    TUrlHandlerDoGenerateTable = class(TUrlHandler)
    public
    procedure Execute; override;
    end;

    implementation

    procedure TUrlHandlerGenerateTable.Execute;
    begin
    AnswerPage(’’, NO_CACHE, 'GenerateTable.html', nil, []);
    Finish;
    end;

    procedure TUrlHandlerDoGenerateTable.Execute;
    var
    PasswordCount : String;
    begin
    ExtractURLEncodedValue(Params, 'PasswordCount', PasswordCount);
    // Here comes the processing and the AnswerPage
    Finish;
    end;

    end.

    The two lines to be inserted in TTutWebAppServerMainForm.FormShow to map the URL are:
    HttpAppSrv1.AddGetHandler('/GenerateTable.html', TUrlHandlerGenerateTable);
    HttpAppSrv1.AddGetHandler('/DoGenerateTable.html', TUrlHandlerDoGenerateTable);
  2. Write the code to generate the requested number of passwords. Here we have two options: either generate all passwords before generating the actual table, or generate the passwords on the fly while generating each table row. Since the first option gives a code easier to understand, I will use it. The passwords will be generated in a TStringList which will be used later when generating each table row.
    Passwords will be generated using Delphi random generator. For simplicity, we will generate passwords containing uppercase and lowercase letters plus all ten digits. Passwords will have 12 characters.
    The code looks like:
    procedure TUrlHandlerDoGenerateTable.GeneratePasswords(PasswordCount: Integer);
    var
    Password : String;
    I : Integer;
    const
    PasswordLen = 12;
    PasswordChars : String = 'abcdefghijklmnopqrstuvwxyz' +
    'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
    '0123456789';
    begin
    RandSeed := GetTickCount;
    FPasswords := TStringList.Create;
    SetLength(Password, PasswordLen);
    while PasswordCount > 0 do begin
    for I := 1 to PasswordLen do
    Password[I] := PasswordChars[1 + Random(Length(PasswordChars))];
    FPasswords.Add(Password);
    Dec(PasswordCount);
    end;
    end;
  3. Now that we have a function to generate the passwords, we may use it into the simple procedure TUrlHandlerDoGenerateTable we wrote before:
    procedure TUrlHandlerDoGenerateTable.Execute;
    var
    PasswordCount : String;
    begin
    ExtractURLEncodedValue(Params, 'PasswordCount', PasswordCount);
    GeneratePasswords(StrToInt(PasswordCount));
    OnGetRowData := GetRowData;
    AnswerPage(’’, NO_CACHE, 'GeneratePasswordResult.html', nil, []);
    Finish;
    end;
    You can see here a new comer: OnGetRowData. This is a kind of event which will be called by AnswerPage whenever a table is found in the template file. A table is marked in a template file using the special tag <#table_rows>. The next step show how to use that tag.
  4. Create a new HTML template file for presenting the password list and name it GeneratePasswordResult.html. We will build a real HTML table here. There is nothing special about the HTML table except we only create one row and surround it with the special tags <#table_rows> and <#/table_rows>. When AnswerPage will find the first tag, it starts repeating the bloc until the second tag. In each iteration it call the OnGetRowData event handler to get the data and know if there are more rows. Here is how the HTML source code looks like in the body of the HTML file:
    <table>
    <tr><td>N°</td><td>Password</td></tr>
    <#table_rows PasswordTable>
    <tr><td><#Row></td><td><#Password></td></tr>
    <#/table_rows>
    </table>
    You see a parameter within <#table_rows>. It is used in OnGetRowData so that Delphi code knows which table is currently built because a single HTML template can have multiple tables and tables may be nested.
  5. Write the OnGetRowData event handler with the following code:
    procedure TUrlHandlerDoGenerateTable.GetRowData(Sender: TObject;
    const TableName: String; Row: Integer; TagData: TStringIndex;
    var More: Boolean; UserData: TObject);
    begin
    More := Assigned(FPasswords) and (Row <= FPasswords.Count);
    if More then begin
    TagData.Add('Row', IntToStr(Row));
    TagData.Add('Password', FPasswords[Row - 1]);
    end;
    end;
    OnGetRowData event handler is called again and again by AnswerPage when a table_rows tag is found. Actually it is called once per data row. AnswerPage stops calling it when the var argument named "more" is set to FALSE. The main purpose of the handler is to call TagData.Add to specify values for the special tags within the table rows. Here we have defined two of them in our template file: "Row" and "Password". The event handler has also to set the var argument More according to the fact that we have more records. Here we set More according to the row index given by the argument "Row" compared to the password stringlist count.
  6. One more step is needed to free the stringlist holding all password. We do that in the destructor:
    destructor TUrlHandlerDoGenerateTable.Destroy;
    begin
    FreeAndNil(FPasswords);
    inherited;
    end;
  7. You can now hit F9 to compile and run the application. Then point your browser to http://127.0.0.1:20080/GenerateTable.html, enter a value and click the button. You you see the table built and filled with the number of passwords you requested.



Summary

In this lesson you learned how to created a template file having a table and to write code to feed the table rows with dynamic data. You know how to use <#table_rows> tag and how to write a OnGetRowData handler to fetch data for your table.


Next lesson: Relocation
Previous lesson: Simple data entry and processing
Tutorial presentation: TutWebAppServer