Simplifying the Special Offers Example

The special offers example you've just seen is an older way of doing things—and it works—but it certainly isn't the only way. This section contains some ideas and examples that have much the same result but use some .NET helper functions and different client-side techniques to display the special offers from the preceding section.

ASP.NET can help you write complex client scripts. The first change you can make is to use a Page class method called RegisterArrayDeclaration. For example, you can condense the For...Next loop in Listing 20.7 that builds the offers array string for the client to these few lines of code:

foreach(XmlElement offer in offers) {

this.RegisterArrayDeclaration("offers", "'" +

offer.SelectSingleNode("duration").InnerText + "'"); this.RegisterArrayDeclaration("offers", "'" +

offer.SelectSingleNode("text").InnerText + "'"); this.RegisterArrayDeclaration("offers", "'" +

offer.SelectSingleNode("font/@name").Value + "'"); this.RegisterArrayDeclaration("offers", "'" +

offer.SelectSingleNode("font/@size").Value + "'"); this.RegisterArrayDeclaration("offers", "'" +

offer.SelectSingleNode("font/@bold").Value + "'"); this.RegisterArrayDeclaration("offers", "'" +

offer.SelectSingleNode("font/@italic").Value + "'"); this.RegisterArrayDeclaration("offers", "'" +

offer.SelectSingleNode("font/@color").Value + "'"); this.RegisterArrayDeclaration("offers", "'" +

offer.SelectSingleNode("url").InnerText + "'"); this.RegisterArrayDeclaration("offers", "'" +

offer.SelectSingleNode("backgroundimage").InnerText + "'");

See the Web Form ch2 0-7.aspx for an example. RegisterArrayDeclaration accepts the name of the client-side array variable (in this case, offers) and the value to place into the array. It outputs a <script> tag containing a JScript array declaration. In the browser, the script generated by the preceding code looks like this:

<script language="javascript"> <!--

var offers = new Array(

'3', 'Housewares on sale until 4:00', 'Microsoft Sans Serif',

'12', 'false', 'true', '#0000ff', 'ch20-6.aspx?item= Housewares',

'images/acindstr.gif', '6', 'Quick Sale on all CDs! Click Now!',

'Arial', '16', 'true', 'false', '#ff0000', 'ch20-6.aspx?item=CDs', 'images/stone.bmp', '4', 'Visit our Garden Center for special end-of-summer deals!', 'Times New Roman', '14', 'true', 'true', '#00ff00', 'ch20-6.aspx?item=Garden%20Center', 'images/acbluprt.gif'); // --> </script>

Although this is slightly less readable than the handcrafted version, it's just as functional and much easier to code and maintain. Note that the code creates each array item as a string by surrounding it with quotes. That's not strictly necessary, because not all the items are strings, but it does illustrate the point that client-side JavaScript, being typeless, converts data types as needed.

Special Offers Example Using Frames

Frames are irritating beasts at best. They slow down page loads, cause multiple requests to your server, and cause you to lose control of the order in which content is displayed. Browsers treat each frame as a separate window. They request content for each frame in the order in which you define the frames, but that doesn't mean the data appears in that order. To create a frames-based application, you need to define the frameset—the set of frames that your application uses to display the content.

For example, in this application, you might define two frames: a narrow top frame to display the special offers and the remainder of the screen, dedicated to other site content. A frameset page may not contain content other than the frames definition and a message for browsers that don't support frames (not much of a consideration these days).

A frameset page is not a Web Form, it's an HTML page; Visual Studio does not automatically treat framesets as Web Forms. With that said, you are free to create a new Web Form, remove its content, and treat it as a frames page if you like. To create a frameset the Visual Studio way, right-click the project name in Solution Explorer, then add a new frameset item. You get a choice of some predefined frameset templates (see Figure 20.5).

Figure 20.5: Predefined frameset templates

You can find an example in the specialOffersFrameset.htm file in the Ch2 0 folder of the CSharpASP project on www.sybex.com. For the example, I used the "horizontal split" layout, which creates two independent frames. You can change the split—the area occupied by each frame—using the HTML designer (see Figure 20.6).

Figure 20.6: Horizontal split frameset template

In the designer, click each frame and set its id property and name property to the same value. You'll need the id property to refer to the frame in script. Each frame accepts the name of a file to request for the frame's contents. You can use the designer to assign an initial file for each frame. Set the src property for each frame to assign the initial file. In this example, the top frame (named top) gets its content from the ch20-8.aspx file, which is identical to ch20-7.aspx except that it adds a second query string parameter Back=false that prevents the ch20-6.aspx file from displaying the Click here to return message. It does that because there's no need to return—the content appears in the bottom frame when you click an offer.

This version also uses a slightly altered version of the specialOffers.js file, called special-Offers2.js, which sets the target property of the anchor tag in the ad to main.

Special Offers Example Using a Data Island

The frames version was somewhat simpler than the original, but if you didn't have to extract the data from the XML file and place it into a client-side array, it would require much less code. In addition, if you could perform an XSLT transform on the client, you wouldn't have to run the rather involved included script (specialOffers.js). You can accomplish both these tasks using data islands.

A data island is an XML document (or an XSLT stylesheet—remember that those are XML documents, too) placed inline in an HTML stream sent to the client. You surround each XML document with <xml id="someID"></xml> tags that you can access and manipulate from script. The <xml> tag can read a file directly using an src attribute. For example, the following tag creates a data island containing the contents of the specialOffers.xml file:

<xml id="specials" src="specialOffers.xml"></xml>

Internet Explorer exposes the XML document as an ActiveX DOMDocument object that's similar to the XmlDocument and XPathDocument objects in .NET. For example:

<xml id="specials" src="specialOffers.xml"></xml>

<script type="text/javascript">

var doc = document.getElementById("specials").XMLDocument; alert(doc.xml); </script>

The preceding script loads and parses the XML in the specialOffers.xml file into a DOMDocument object. The script sets a variable named doc to refer to the DOMDocument object and then uses the JavaScript alert method to display the contents in a message box.

When the browser parses the <xml> tag, it requests the document content from the server. By placing two <xml> tags on the page, you can load an XML document and a corresponding XSLT stylesheet and then perform the transform on the client side with the help of a little JavaScript.

Note Unfortunately, browsers—even IE—aren't quite up to .NET standards yet. IE ships with the COM-based MSXML parser. Even more unfortunately, not all versions of IE ship with the same parser. For example, IE 5x ships with a very early version of MSXML that doesn't understand the XSLT namespace. IE 6x ships with the MSXML3 parser, which works fine with the XSLT namespace. The most current version is MSXML4 (sp1), which adds XML Schema support and improves the performance of parsing and transformation operations somewhat. To use XSLT client-side transforms with versions of IE earlier than IE 6, you must install the MSXML parser (downloadable for free from the MSDN XML Developer Center). For some versions of the parser, you must also download and run a small program named xmlinst.exe, also downloadable from MSDN, which lets IE use the XSLT namespace.

The Web Form ch2 0-9.aspx shows an example using the client-side transform technique. In this version, there's no code in the code-behind class at all; all the code is HTML and script placed directly into the ASPX file. Listing 20.9 shows the ch2 0-9.aspx file code.

Listing 20.9: The Web Form ch20-9.aspx Uses a Client-Side XSLT Transform to Display the Special Offers (ch20-9.aspx.cs)

<%@ Page Language="c#" AutoEventWireup="false"

Codebehind="ch2 0-9.aspx.cs" Inherits="CSharpASP.ch2 0.ch2 0_9" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html>

<meta name="GENERATOR" content="Microsoft Visual Studio.NET 7.0">

<meta name="CODE_LANGUAGE" content="C#">

<meta name="vs defaultClientScript" content="JavaScript"> <meta name="vs targetSchema"

content="http://schemas.microsoft.com/intellisense/ie5"> <script type="text/javascript" language="javascript"> var xml; var xsl;

function XMLloaded() {

// load the DOMDocument object for the XML document xml = document.getElementById("specialsXML").XMLDocument;

function XSLTloaded() {

// load the DOMDocument object for the stylesheet xsl = document.getElementById("specialsXSLT").XMLDocument;

function showSpecial(offerNumber) { var s;

var duration;

// set the stylesheet parameter value that controls // which offer to show.

xsl.selectSingleNode("//xsl:param[@name='offerNumber']").text = offerNumber; s = xml.transformNode(xsl);

document.getElementById("specialOffer").innerHTML = s; duration = xml.selectSingleNode("offers/offer[" +

(offerNumber - 1) + "]/duration").text; duration = parseInt(duration); offerNumber = parseInt(offerNumber); offerNumber += 1;

if (offerNumber > (xml.selectNodes("offers/offer").length))

offerNumber = 1;

window.setTimeout("showSpecial(" + offerNumber + ")", duration * 1000);

<body MS_POSITIONING="GridLayout" onload="showSpecial(1)"> <xml id="specialsXML" src="specialOffers.xml"

ondataavailable="XMLloaded();"></xml> <xml id="specialsXSLT" src="specialOffers.xsl"

ondataavailable="XSLTloaded();"></xml> <form id="Form1" method="post" runat="server"> <div id="specialOffer"></div> </form> </body> </html>

Here's what happens. Look at the last part of the listing, after the </head> tag. When the browser parses the first <xml> tag, it retrieves the specialoffers.xml file from the server. Immediately after that file is completely loaded, it fires the ondataavailable event, which is bound to the XMLloaded () JavaScript function at the top of the listing. The browser loads the XSLT specialoffers.xsl file the same way but calls the XSLTloaded() function instead.

<script type="text/javascript" language="javascript"> var xml; var xsl;

function XMLloaded() {

xml = document.getElementById("specialsXML").XMLDocument;

function XSLTloaded() {

xsl = document.getElementById("specialsXSLT").XMLDocument;

Note that the script defines two global (page-level) variables named xml and xsl. These variables will hold XML DOMDocument objects corresponding to the XML document and the XSLT stylesheet. Both functions perform the same task—they retrieve a DOMDocument object instance from the <xml> tag.

After instantiating these objects, the <body> tag fires the onload event. That event is bound to the showSpecial(offerNumber) JavaScript function:

<body MS_POSITIONING="GridLayout" onload="showSpecial(1)">

You can translate this as "When the document finishes loading, call the showSpecial function to show the first special offer (the parameter 1)."

The showSpecial() function sets a parameter value in the XSLT file—also named offerNumber—to the value passed in the offerNumber parameter to the showSpecial() function. The XSLT stylesheet uses the offerNumber parameter value to extract the appropriate <offer> node from the XML document.

xsl.selectSingleNode("//xsl:param[@name='offerNumber']").text = offerNumber;

Next, the script performs the transform by calling the transformNode() method of the DOMDocument object and saves the string output from that transform in the variable s. The output is the HTML to display the special offer:

s = xml.transformNode(xsl);

The script assigns the results of the transform to the innerHTML property of the <div id="special-offer"> tag toward the bottom of Listing 20.9:

document.getElementById("specialOffer").innerHTML = s;

That displays the special offer, but you still need to retrieve the duration—the number of seconds to display that offer—from the DOMDocument:

duration = xml.selectSingleNode("offers/offer[" +

(offerNumber - 1) + "]/duration").text; duration = parseInt(duration);

The script retrieves the value and assigns it to the duration variable. When retrieved, the variable is a string; therefore, the script transforms it into an integer using the parseInt() method.

Finally, the script needs to call itself to display the next special offer. Therefore, it must increment the offerNumber variable and then call the window.setTimeout() function, passing its own name and the duration in milliseconds. It also needs to ensure that offerNumber doesn't exceed the number of offers in the XML document; otherwise, you'll get an error. To increment offerNumber, first ensure that it's an integer value:

offerNumber = parseInt(offerNumber); offerNumber += 1;

if (offerNumber > (xml.selectNodes("offers/offer").length)) { offerNumber = 1;

Finally, it calls the window.setTimeout() function using the incremented offerNumber and the duration value multiplied by 1,000 to turn it into milliseconds:

window.setTimeout("showSpecial(" + offerNumber + ")", duration * 1000);

As you can see, you have a multitude of choices for doing almost anything. Other options involve running the transform on the server. For example, you could write a Web Service that returns the special offer HTML given the offer number. You could use an <iframe> tag to display the offers rather than a <div>. Doing it that way, you could use a standard ASPX file to deliver the HTML and just set the src property of the <iframe> to change the contents from one special offer to another.

Was this article helpful?

0 0

Post a comment