Web Database Applications

Web Database Applications

As in Delphi's libraries, a significant portion of IntraWeb's available controls relates to the development of database applications. The IntraWeb Application Wizard has a version that allows you to create an application with a data module—a good starting point for the development of a database application. In such a case, the application's predefined code creates an instance of the data module for each session, saving it in the session's data.

Here is the predefined TUserSession class (and its constructor) for an IntraWeb application with a data module:

  TUserSession = class(TComponent)
    DataModule1: TDataModule1;
    constructor Create(AOwner: TComponent); override;
constructor TUserSession.Create(AOwner: TComponent);
  Datamodule1 := TDatamodule1.Create(AOwner);

The unit of the data module doesn't have a global variable for it; if it did, all the data would be shared among all sessions, with severe risks of trouble in case of concurrent requests in multiple threads. However, the data module already exposes a global function having the same name as the global variable Delphi would use, accessing the current session's data module:

function DataModule1: TDataModule1;
  Result := TUserSession(RWebApplication.Data).Datamodule1;

This means you can write code like the following:


But instead of accessing a global data module, you are using the current session's data module.

In the first sample program featuring database data, called IWScrollData, I've added to the data module a SimpleDataSet component and to the main form an IWDBGrid component with the following configuration:

object IWDBGrid1: TIWDBGrid
  Anchors = [akLeft, akTop, akRight, akBottom]
  BorderSize = 1
  CellPadding = 0
  CellSpacing = 0
  Lines = tlRows
  UseFrame = False
  DataSource = DataSource1
  FromStart = False
  Options = [dgShowTitles]
  RowAlternateColor = clSilver
  RowLimit = 10
  RowCurrentColor = clTeal

The most important settings are the removal of a frame hosting the control with its own scroll bars (the UseFrame property), the fact that the data is displayed form the current data set position (the FromStart property), and the number of rows to be displayed in the browser (the RowLimit property). In the user interface, I've removed vertical lines and colored alternate rows. I also had to set up a color for the current row (the RowCurrentColor property); otherwise the alternate colors won't appear to work properly, since the current row is the same color as the background rows, regardless of its position (set the RowCurrentColor property to clNone to see what I mean). These settings produce the effect you can see in Figure 21.7 or by running the IWScrollData example.

Click To expand Figure 21.7: The data-aware grid of the IWScrollData example

The program opens the data set when the form is created, using the data set hooked to the current data source:

procedure TformMain.IWAppFormCreate(Sender: TObject);

The example's relevant code is in the button code, which can be used to move through the data showing the following page or returning to the previous one. Here is the code for one of the two methods (the other is omitted, because it's very similar):

procedure TformMain.btnNextClick(Sender: TObject);
  i: Integer;
  nPos := nPos + 10;
  if nPos > DataSource1.DataSet.RecordCount - 10 then
    nPos := DataSource1.DataSet.RecordCount - 10;
  for i := 0 to nPos do

Linking to Details

The grid of the IWScrollData example shows a single page of a table's data; buttons let you scroll up and down the pages. An alternative grid style in IntraWeb is offered by framed grids, which can move larger amounts of data to the web browser within a screen area of a fixed size using a frame and an internal scroll bar, as a Delphi ScrollBox control does. This is demonstrated by the IWGridDemo example.

The example customizes the grid in a second powerful way: It sets the Columns collection property of the grid. This setting allows you to fine-tune the output and behavior of specific columns, for example by showing hyperlinks or handling clicks on items or title cells. In the IWGridDemo example, one of the columns (the last name) is turned into a hyperlink; the employee number is passed as a parameter to the follow-up command, as you can see in Figure 21.8.

Click To expand
Figure 21.8:  The main form of the IWGridDemo example uses a framed grid with hyperlinks to the secondary form.

Listing 21.1 shows a summary of the grid's key properties. Notice in particular the last name column, which as mentioned has a linked field (which turns the cell's text into a hyperlink) and an event handler responding to its selection. In this method, the program creates a secondary form in which a user can edit the data:

procedure TGridForm.IWDBGrid1Columns1Click(ASender: TObject;
  const AValue: String);
  with TRecordForm.Create (WebApplication) do
    StartID := AValue;
Listing 21.1: Properties of the IWDBGrid in the IWGridDemo Example
Start example
object IWDBGrid1: TIWDBGrid
  Anchors = [akLeft, akTop, akRight, akBottom]
  UseFrame = True
  UseWidth = True
  Columns = <
      Alignment = taLeftJustify
      BGColor = clNone
      DoSubmitValidation = True
      Font.Color = clNone
      Font.Enabled = True
      Font.Size = 10
      Font.Style = []
      Header = False
      Height = '0'
      VAlign = vaMiddle
      Visible = True
      Width = '0'
      Wrap = False
      BlobCharLimit = 0
      CompareHighlight = hcNone
      DataField = 'FIRST_NAME'
      Title.Alignment = taCenter
      Title.BGColor = clNone
      Title.DoSubmitValidation = True
      Title.Font.Color = clNone
      Title.Font.Enabled = True
      Title.Font.Size = 10
      Title.Font.Style = []
      Title.Header = False
      Title.Height = '0'
      Title.Text = 'FIRST_NAME'
      Title.VAlign = vaMiddle
      Title.Visible = True
      Title.Width = '0'
      Title.Wrap = False
      DataField = 'LAST_NAME'
      LinkField = 'EMP_NO'
      OnClick = IWDBGrid1Columns1Click
      DataField = 'HIRE_DATE'
      DataField = 'JOB_CODE'
      DataField = 'JOB_COUNTRY'
      DataField = 'JOB_GRADE'
      DataField = 'PHONE_EXT'
  DataSource = DataSource1
  Options = [dgShowTitles]
End example

By setting the second form's StartID property, you can locate the proper record:

procedure TRecordForm.SetStartID(const Value: string);
  FStartID := Value;
  DataSource1.DataSet.Locate('EMP_NO', Value, []);

The IWDBGrid columns have also an OnTitleClick event you can handle to sort the data or perform other operations on the column.

The secondary form is hooked to the same data module as the main form. So, after the database data is updated, you can see it in the grid (but the updates are kept only in memory, because the program doesn't have an ApplyUpdates call). The secondary form uses a few edit controls and a navigator, provided by IntraWeb. You can see this form at run time in Figure 21.9.

Click To expand
Figure 21.9: The secondary form of the IWGridDemo example allows a user to edit the data and navigate through records.

Moving Data to the Client Side

Regardless of how you use it, the IWDBGrid component produces HTML with the database data embedded in the cells, but it cannot work on the data on the client side. A different component (or a set of IntraWeb components) allows you to follow a different model. The data is sent to the browser in a custom format, and the JavaScript code on the browser populates a grid and operates on the data, moving from record to record without asking more data to the server.


This architecture is similar to Delphi's native Internet Express architecture, which I'll cover in Chapter 22 ("Using XML Technologies").

You can use several IntraWeb components for a client-side application, but these are the most important ones:

IWClientSideDataSet  An in-memory dataset you define by setting the ColumnNames and Data properties within your program's code. In future updates, you will be able to edit client-side data, sort it, filter it, define master-detail structures, and more.

IWClientSideDataSetDBLink  A data provider you can connect to any Delphi dataset, connecting it with the DataSource property.

IWDynGrid  A dynamic grid component connected to one of the two previous components using the Data property. This component moves all the data to the browser and can operate on it on the client via JavaScript.

There are other client-side components in IntraWeb, such as IWCSLabel, IWCSNavigator, and IWDynamicChart (which works only with Internet Explorer). As an example of using this approach, I've built the IWClientGrid example. The program has little code, because there is a lot available in the components being used. Here are the core elements of its main form:

object formMain: TformMain
  SupportedBrowsers = [brIE, brNetscape6]
  OnCreate = IWAppFormCreate
  object IWDynGrid1: TIWDynGrid
    Align = alClient
    Data = IWClientSideDatasetDBLink1
  object DataSource1: TDataSource
    Left = 72
    Top = 88
  object IWClientSideDatasetDBLink1: TIWClientSideDatasetDBLink
    DataSource = DataSource1

The dataset from the data module is hooked to the DataSource when the form is created. The resulting grid, shown in Figure 21.10, allows you to sort the data on any cell (using the small arrow after the column title) and filter the data displayed on one of the field's possible values. In the figure, for example, you can sort the employee data by last name and filter it by country and job grade.

Click To expand
Figure 21.10: The grid of the IWClientGrid example supports custom sorting and filtering without re-fetching the data on the web server.

This functionality is possible because the data is moved to the browser within the JavaScript code. Here is a snippet of one of the scripts embedded in the page's HTML:

<script language="Javascript1.2">
var IWDYNGRID1_TitleCaptions =
var IWDYNGRID1_CellValues = new Array();
IWDYNGRID1_CellValues[0] = [2,'Robert','Nelson','332','600','VP',2,'USA'];
IWDYNGRID1_CellValues[1] = [4,'Bruce','Young','233','621','Eng',2,'USA'];
IWDYNGRID1_CellValues[2] = [5,'Kim','Lambert','22','130','Eng',2,'USA'];
IWDYNGRID1_CellValues[3] = [8,'Leslie','Johnson','410','180','Mktg',3,'USA'];
IWDYNGRID1_CellValues[4] = [9,'Phil','Forest','229','622','Mngr',3,'USA'];

The reason to use this JavaScript-based approach, instead of an XML-based approach featured by other similar technologies, is that only Internet Explorer has support for XML data islands. Mozilla and Netscape lack this feature and have limited XML support in general. Mimicking it in JavaScript, as Internet Explorer does, is very expensive at run time.

Part I: Foundations