|
Generating HTMLThe Hypertext Markup Language, better known by its acronym HTML, is the most widespread format for content on the Web. HTML is the format web browsers typically read; it is a standard defined by the W3C (World Wide Web Consortium, www.w3.org), which is one of the bodies controlling the Internet. The HTML standard document is available on www.w3.org/MarkUp along with and some interesting links. Delphi's HTML Producer ComponentsDelphi's HTML producer components (on the Internet page of the Component Palette) can be used to generate the HTML files and particularly to turn a database table into an HTML table. Many developers believe that the use of these components makes sense only when writing a web server extension. Although they were introduced for this purpose and are part of the WebBroker technology, you can still use three out of the five producer components in any application in which you must generate a static HTML file. Before looking at the HtmlProd example, which demonstrates the use of these HTML producer components, let me summarize their role:
Producing HTML PagesA very simple example of using tags (introduced by the # symbol) is creating an HTML file that displays fields with the current date or a date computed relative to the current date, such as an expiration date. If you examine the HtmlProd example, you'll find a PageProducer1 component with internal HTML code, specified by the HTMLDoc string list: <html> <head> <title>Producer Demo</title> </head> <body> <h1>Producer Demo</h1> <p>This is a demo of the page produced by the <b><#appname></b> application on <b><#date></b>.</p> <hr> <p>The prices in this catalog are valid until <b><#expiration days=21></b>.</p> </html> </body>
The Demo Page button copies the PageProducer component's output to the Text of a Memo. As you call the Content function of the PageProducer component, it reads the input HTML code, parses it, and triggers the OnTag event handler for every special tag. In the handler for this event, the program checks the value of the tag (passed in the TagString parameter) and returns a different HTML text (in the ReplaceText reference parameter), producing the output shown in Figure 19.7. Figure 19.7: The output of the HtmlProd example, a simple demonstra-tion of the Page-Producer component, when the user clicks the Demo Page button procedure TFormProd.PageProducer1HTMLTag(Sender: TObject; Tag: TTag; const TagString: String; TagParams: TStrings; var ReplaceText: String); var nDays: Integer; begin if TagString = 'date' then ReplaceText := DateToStr (Now) else if TagString = 'appname' then ReplaceText := ExtractFilename (Forms.Application.Exename) else if TagString = 'expiration' then begin nDays := StrToIntDef (TagParams.Values['days'], 0); if nDays <> 0 then ReplaceText := DateToStr (Now + nDays) else ReplaceText := '<i>{expiration tag error}</i>'; end; end; Notice in particular the code I've written to convert the last tag, #expiration, which requires a parameter. The PageProducer places the entire text of the tag parameter (in this case, days=21) in a string that's part of the TagParams list. To extract the value portion of this string (the portion after the equal sign), you can use the Values property of the TagParams string list and search for the proper entry at the same time. If it can't locate the parameter or if the parameter's value isn't an integer, the program displays an error message.
Producing Pages of DataThe HtmlProd example also has a DataSetPageProducer component, which is connected with a database table and with the following HTML source code: <html><head><title>Data for <#name></title></head> <body> <h1><center>Data for <#name></center></h1> <p>Capital: <#capital></p> <p>Continent: <#continent></p> <p>Area: <#area></p> <p>Population: <#population></p><hr> <p>Last updated on <#date><br> HTML file produced by the program <#program>.</p> </body></html> By using tags with the names of the connected dataset's fields (the usual COUNTRY.DB database table), the program automatically gets the value of the current record's fields and replaces them automatically. This produces the output shown in Figure 19.8; the browser is connected to the HtmlProd example working as an HTTP server, as I'll discuss later. In the source code of the program related to this component, there is no reference to the database data: procedure TFormProd.DataSetPageProducer1HTMLTag(Sender: TObject; Tag: TTag; const TagString: String; TagParams: TStrings; var ReplaceText: String); begin if TagString = 'program' then ReplaceText := ExtractFilename (Forms.Application.Exename) else if TagString = 'date' then ReplaceText := DateToStr (Date); end; Producing HTML TablesThe third button in the HtmlProd example is Print Table. This button is connected to a DataSetTableProducer component, again calling its Content function and copying its result to the Text of the Memo. By connecting the DataSet property of the DataSetTableProducer to ClientDataSet1, you can produce a standard HTML table. The component by default generates only 20 rows, as indicated by the MaxRows property. If you want to get all of the table's records, you can set this property to -1—a simple but undocumented setting. To make the output of this producer component more complete, you can perform two operations. The first is to provide some Header and Footer information (to generate the HTML heading and closing elements) and add a Caption to the HTML table. The second is to customize the table by using the setting specified by the RowAttributes, TableAttributes, and Columns properties. The property editor for the columns, which is also the default component editor, allows you to set most of these properties, providing at the same time a nice preview of the output, as you can see in Figure 19.9. Before using this editor, you can set up properties for the dataset's fields using the Fields editor. This is how, for example, you can format the output of the population and area fields to use thousands separators. Figure 19.9: The editor of the DataSetTable-Producer compo-nent's Columns property provides you with a preview of the final HTML table (if the data-base table is active). You can use three techniques to customize the HTML table, and it's worth reviewing each of them:
In the HtmlProd example, I've used a handler for this event to turn the text of the Population and Area columns to bold font and to a red background for large values (unless it is the header row). Here is the code: procedure TFormProd.DataSetTableProducer1FormatCell( Sender: TObject; CellRow, CellColumn: Integer; var BgColor: THTMLBgColor; var Align: THTMLAlign; var VAlign: THTMLVAlign; var CustomAttrs, CellData: String); begin if (CellRow > 0) and (((CellColumn = 3) and (Length (CellData) > 8)) or ((CellColumn = 4) and (Length (CellData) > 9))) then begin BgColor := 'red'; CellData := '<b>' + CellData + '</b>'; end; end; The rest of the code is summarized by the settings of the table producer component, including its header and footer, as you can see by opening the source code of the HtmlProd example. Using Style SheetsThe latest incarnations of HTML include a powerful mechanism for separating content from presentation: Cascading Style Sheets (CSS). Using a style sheet, you can separate the formatting of the HTML (colors, fonts, font sizes, and so on) from the text displayed (the content of the page). This approach makes your code more flexible and your website easier to update. In addition, you can separate the task of making the site graphically appealing (the work of a web designer) from automatic content generation (the work of a programmer). Style sheets are a complex technique, in which you give formatting values to the main types of HTML sections and to special "classes" (which have nothing to do with OOP). Again, see an HTML reference for the details. You can update table generation in the HtmlProd example to include style sheets by providing a link to the style sheet in the Header property of a second DataSetTableProducer component: <link rel="stylesheet" type="text/css" href="test.css"> You can then update the code of the OnFormatCell event handler with the following action (instead of the two lines changing the color and adding the bold font tag): CustomAttrs := 'class="highlight"'; The style sheet I've provided (test.css, available in the source code of the example) defines a highlight style, which has the bold font and red background that were hard-coded in the first DataSetTableProducer component. The advantage of this approach is that now a graphic artist can modify the CSS file and give your table a nicer look without touching its code. When you want to provide many formatting elements, using a style sheet can also reduce the total size of the HTML file. This is an important element that can reduce download time. Dynamic Pages from a Custom ServerThe HtmlProd component can be used to generate static HTML files; it doubles as a web server, using an approach similar to that demonstrated in the HttpServ example, but in a more realistic context. The program accesses the request of one of the possible page producers, passing the name of the component in a request. This is a portion of the IdHTTPServer component's OnCommandGet event handler, which uses the FindComponent method to locate the proper producer component: var Req, Html: String; Comp: TComponent; begin Req := RequestInfo.Document; if Req [1] = '/' then Req := Copy (Req, 2, 1000); // skip '/' Comp := FindComponent (Req); if (Req <> '') and Assigned (Comp) and (Comp is TCustomContentProducer) then begin ClientDataSet1.First; Html := TCustomContentProducer (Comp).Content; end; ResponseInfo.ContentText := Html; end; In case the parameter is not there (or is not valid), the server responds with an HTML-based menu of the available components: Html := '<h1>HtmlProd Menu<h1><p><ul>'; for I := 0 to ComponentCount - 1 do if Components [i] is TCustomContentProducer then Html := Html + '<li><a href="/' + Components [i].Name + '">' + Components [i].Name + '</a></li>'; Html := Html + '</ul></p>'; Finally, if the program returns a table that uses CSS, the browser will request the CSS file from the server; so, I've added some specific code to return it. With the proper generalizations, this code shows how a server can respond by returning files, and also how to indicate the MIME type of the response (ContentType): if Pos ('test.css', Req) > 0 then begin CssTest := TStringList.Create; try CssTest.LoadFromFile(ExtractFilePath(Application.ExeName) + 'test.css'); ResponseInfo.ContentText := CssTest.Text; ResponseInfo.ContentType := 'text/css'; finally CssTest.Free; end; Exit; end; |
|
Copyright © 2004-2024 "Delphi Sources" by BrokenByte Software. Delphi Programming Guide |
|