Programmer's Guide



Chapter 10   Programming Pages

This chapter explains page concepts in more depth and describes some of the common tasks you perform as you program pages. It covers the following topics:

Choosing a page programming model   Top of page

Programmers use many approaches to develop pages on the Web. This section describes some of the possible programming models and techniques for designing dynamically generated HTML pages in SilverStream.

Understanding Web programming models   Top of page

Programmers can model Web applications in many different ways. You can choose a model based on the pattern of requests and responses between the browser and the server. The Single-URL model, the traditional Multi-URL model, and the Multi-URL with Redirect model represent three common approaches.

The Single-URL model

In the Single-URL model, the user begins working with a Web application by requesting the URL. The HTML document returned by the server contains the first step of the application. The browser sends the result of the user's interaction with the page to the originally requested URL on the server. This time, however, the server returns a different HTML document or a modified version of the HTML document. This back-and-forth interaction with a single URL continues as the user works with the application.

A program, servlet, or CGI script implements the server side of this interaction. It interprets the user's request, then returns or generates the next appropriate HTML document. SilverStream facilitates programming in this model with:

In general, single URL applications can sometimes cause coding difficulty because a single location must hold all of the logic for the application.

The traditional Multi-URL model

A specific URL identifies each page in the traditional Multi-URL model. Each page contains links in its HTML, pointing to the next page in the application sequence. When the user asks for the next step in the application, the browser issues its request to the next page's URL, which processes the user's input from the step just completed and returns the HTML for the new step.

A separate HTML document or CGI script represents each step in the application. SilverStream supports this model by providing a page property that specifies the URL to which the browser sends the page information.

SilverStream also makes it easy to construct HTML links containing parameters based on information displayed in a page.

This type of multi-URL application has two common difficulties:

The multi-URL with Redirect model

HTTP allows a server to redirect a browser to a different URL than the one requested. That is, a browser requests a particular URL, the server replies with a redirection message, and the browser automatically switches to the redirected URL.

This allows for a slightly different multi-URL programming model. In the Multi-URL with Redirect model, a submit sends the request to the page's own URL. The server then either replies with the same page (perhaps with slight modifications), or with a redirect message that sends the browser to the next page. SilverStream recommends this model, which is the default model in the Page Designer. The Page Wizard constructs this type of form page automatically, allowing the user to browse database records by making multiple requests to the same URL.

This model addresses three difficulties with traditional multi-URL programming:

In addition, this technique encourages an application structure in which the user remains on the same page for several submissions. The server simply returns the same page with different information. This way, you can structure a page like a traditional client/server form, in which the user interface is constant and only the displayed data changes.

Maintaining page state   Top of page

When a Web site implements the redirect programming technique described previously, the browser sends multiple requests to the same URL but gets back different responses for each request. For simple applications, this interaction may just reflect changes in information entered by the user. In more complex applications, the server must remember the state of the page sent to the user to know how to process the user's next request. For example, if a page displays an application requiring multiple steps and the user clicks on a Continue button, the server must know which step the user was processing to display the next step.

Choosing a storage mechanism for the page state

You can use several techniques for representing page state. You might store the page state in:

Storing state in URL parameters

You can track the page state by adding parameters to the URL that the user visits. For example, the URL could contain the parameter StepNumber=5 when the user is processing the fifth step. The link to the next step could contain the same page URL, but with the parameter StepNumber=6.

Even with the redirect model, the URL can represent the page state. As the browser sends each submission to the server, the URL parameters tell the server what state the page is in. The server can respond with a redirect to the same URL, but with updated parameters.

Storing the page state in URL parameters has these limitations:

In addition, putting the page state in the URL parameters allows the user to bookmark the page, along with its state. This might or might not be a desired feature of the Web application.

Storing state in hidden HTML form fields

Another technique for storing the page state is to create HTML form fields on the page and store the state in them, since the browser sends the values of all HTML form fields to the server with each request. You can mark the fields hidden so that the user does not see the state information, but the HTML document still retains them. This technique does not suffer from the limits imposed by browser URL length limits.

One disadvantage of storing the page state in hidden form fields is that an application needing a lot of information to describe the page's state generates a large HTML document, increasing the download time for each page. Also, as with URL parameters, HTML form fields can contain only strings, thus limiting the page state storage to strings. The page must also include explicit code to read the state out of the hidden fields when the browser requests a page, as well as to write the new state into the hidden field before the server returns the next version of the page to the browser.

Storing state in cookies

Cookies can also store page state, in accordance with the HTTP standard. A cookie is a piece of information sent by a Web server to a browser, which is subsequently sent back to the server with each additional browser request. Cookies allow the server to keep track of client sessions and optionally maintain information about the client across multiple client connections.

A cookie that has a specific expiration date is known as a persistent cookie. Persistent cookies are stored in a cookie file on the machine where the browser is running. A cookie that does not have an expiration date is known as a session cookie. Session cookies are maintained in memory for the duration of the browser session rather than on disk. SilverStream uses session cookies to manage user sessions.

Storing state in server memory

SilverStream has built-in support for the notion of a user session. When a browser first connects to the server, the server creates a session object for that connection. The session caches information requested by the browser. When the browser requests a dynamically generated HTML page, the server caches the page object in the session as well. This allows the page object to retain its state information in the server's memory. The server passes each subsequent request from the same connection for the same page to the same page object, which can process the request based on its current in-memory state. The page object remembers its displayed values as well as any instance variables you've defined to store additional state information.

Storing state in server memory provides more flexibility than other techniques because it does not limit the type of information representing the state. URL parameters and HTML fields can only contain strings, while in-memory objects on the server can be any type. On the other hand, storing the page state in server memory does increase the amount of memory that the server requires for each page.

When the browser disconnects from the server, the server discards the session, along with any state that is stored in it. Unfortunately, the browser might not notify the server when it is disconnecting. For this reason, the server automatically discards idle sessions after a certain timeout period.

NOTE   The server automatically discards an idle session after five minutes. The administrator can adjust this parameter.

The server caches the in-memory page state for as long as it retains the user session. If the user makes frequent requests, the session and cached pages are kept in memory. However, if the user leaves the browser idle for longer than the timeout period, the server discards the session and the cached pages along with it.

Many applications run satisfactorily with just storing the page state in the session. However, if the server crashes or the user closes the browser, the server loses the user's application state. The user then starts over the next time the browser requests a page in the application. If fault tolerance is a requirement of your application, you can use a persistent storage mechanism (such as a database or persistent cookies) to protect against the server going down.

Storing state in a database

A disadvantage of storing page state in server memory is that the server loses the page state if it crashes or if the server flushes the page from its cache (see the "Understanding SilverStream Page Caching" section). Alternatively, you can store page state in a database, keyed by identifying user information, such as a user cookie or a user's login ID. This provides a more persistent storage mechanism than server memory, making it possible for an HTML page to support session-level failover. However, storing page state in a database can degrade server performance, since the page state must be read from and written to the database on each request.

    The pgSessionLevelFailover page in the Examples3_HTML database shows how to add session-level failover support to a page. See Application Techniques>HTML Client Techniques in the help system: Implementing Persistent Session-Level Failover.

Writing code on events   Top of page

Understanding the sequence of event firing is important to know where to write code for a page. When the browser requests a page, events fire in the following order:

  1. The pageRequestBegin event fires before the server processes the request.

  2. The server processes any information submitted as part of the request. Typically, this involves passing any values entered by the user in HTML form controls to the in-memory objects representing those controls. These objects fire the appropriate events, such as valueChanged.

  3. After all values have been passed, the submit control fires a pageActionPerformed event.

  4. Unless the page was redirected, the pageGenerateBegin event fires, just before the server generates the HTML and JavaScript for the response.

  5. After the server generates HTML and JavaScript, but just before it returns the response to the browser, the pageRequestEnd event fires.

In most cases, programmers code the valueChanged and pageActionPerformed events for the individual controls, updating the page state or writing data to the database as appropriate. Sometimes programmers write code that executes each time a page is requested, updating the page based on the state of several controls. In this case, the best choice of events is the pageGenerateBegin event. At the time this event fires, the in-memory controls reflect all input from the user, and changes to controls can still show in the generated HTML. The pageRequestBegin event typically occurs too early for general purpose code, since the server has not yet processed the user input. The pageRequestEnd event usually occurs too late, since the HTML and JavaScript already has been generated and changes to controls will not be reflected in the response.

Handling page transitions in multi-page applications   Top of page

A complex Web application typically contains multiple steps, each one corresponding to a page. The user interacts with a particular page for awhile, then moves to the next page after completing the current step.

For example, consider a shopping cart application. In the first step, the user browses a list of products, selecting the ones to order and adding them to the shopping cart. After selecting all the desired products, the user moves to the second step, choosing payment options. After choosing a payment method, the user moves to the third step, verifying and confirming the order, and then to the fourth step, the thank you page.

In the default SilverStream programming model, a single page could represent each of these steps. The product ordering page has a list of products and a list of the contents of the shopping cart. As the user selects products and clicks on a button to add them to the shopping cart, the page refreshes to display the new information. The contents of the shopping cart are part of the page's state, stored in memory on the server. When the user accepts the contents of the shopping cart, a different button tells the page to redirect the browser to the next step in the application, the payment page. The user interacts with the payment page (which might appear multiple times if, for example, the user does not enter complete credit card information the first time). After the user completes the payment information, the payment page redirects the browser to the order confirmation page. The confirm button commits the order and redirects the browser to the thank you page.

Note that in this example, each page was requested multiple times before the next page was requested. Each step in the application flow involved multiple requests to the server.

Initializing a step

To function correctly, a complex, multiple-step application must include logic that determines when to initialize the state for a step and when to move to the next step.

For example, even after the user fills the shopping cart and moves to the payment step, the browser's Back button returns the user to the shopping cart page. Presumably, the application designer would still want the shopping cart to be full, since the user might want to add items to the cart. However, after the user reaches the thank you step, returning to the shopping cart page requires different results. Since the user has already placed the order, the shopping cart needs to be cleared out. In other words, the server must re-initialize the shopping cart step.

How can the shopping cart page tell whether a request results from:

To answer this question, the application designer must understand the different scenarios in which a user can reach a page. The scenarios differ based on application structure. The following table describes typical scenarios. These different scenarios describe transitions from one step to another, or step transitions.

Scenario

Action to take

The page is not initialized, and a required previous step is not complete.

The page has been erroneously requested; redirect to an error page.

The page is not initialized, and all required previous steps are complete.

Initialize page state.

The page is initialized, and the user is working with it.

The page is requested as part of the processing of its step.

The user has completed the step for the page but can still return to the page.

Allow continued processing of the page.

The user has completed enough steps and must not return to the page.

Display an error.

The user has completed the entire application and is going through the steps again.

Initialize the page if the required previous steps are complete. Otherwise, redirect to an error page.

Managing step transitions

An application can detect and manage step transitions by using the application state in the session, calling methods on pages, or using a transition ID.

Storing application state in the session object

You can use the session object to store application state data. This is similar to storing state in page instance variables, but pages share information more easily. Call the setSessionValue() and getSessionValue() methods to store and retrieve information in the session object.

You can manage step transitions by storing an identifier in the session for the step on which the user is currently working. Using this information, each page can decide whether to initialize its state, retain its current state, or display an error. When a page transitions to a new step, it first sets the application state identifier in the session accordingly.

It is easy to implement this technique, though some applications might require more flexibility.

Calling methods to manage transitions

All pages have a method called getPage(), which returns an AgpPage object from the session given its name. If the completion of a particular step must re-initialize a previous step (for example, completing the payment confirmation step requires clearing the shopping cart step), the application can re-initialize the page by casting it to an appropriate type or interface and calling methods on it to cause its re-initialization.

A drawback to this technique is that multiple pages contain the scattered logic for the application flow, causing possible maintenance difficulties. For example, the payment confirmation step contains code to re-initialize the shopping cart step. If the application subsequently changes so that two steps are required for the shopping cart step, then the code in the payment confirmation step must also be updated. One way to handle this maintenance problem is to put all the logic for handling application flow into a single utility class that all the pages call whenever making a step transition. This utility class can function as the central repository for knowledge about the interdependencies of pages.

Storing a transition ID

You can also manage step transitions by passing a unique transition ID on every valid transition between steps. Each page stores the last valid transition ID it received. Whenever it receives a request without a transition ID, it knows the request is outside of the planned application flow. When the page receives a request with a transition ID that matches its stored value, it knows that the user is interacting with it directly. When it receives a request with a new transition ID, the page knows that it is being re-entered from the previous step and that it must re-initialize the step.

It is easy to pass a transition ID on the URL of the page. When the shopping cart step redirects the browser to the payment information step, it can add a URL parameter in the format TransitionID=someUniqueString. You can specify a Hashtable of parameters with the showPage() and getPageURL() methods. To get the value of a parameter on the URL, call the getParameterValues() method on the req request object, obtained via the getCurrentRequest() method, along with the name of the parameter. The Thread.currentTimeMillis() method returns a long integer that is fairly reliably unique from one call to the next.

Creating a page   Top of page

When you are ready to actually start building pages for your application, bring up the SilverStream Designer. Select Pages, then choose the New icon to create the new page. The Page Wizard asks you to choose a layout for your page. The layouts the wizard provides are described in the following sections. You can always change a format later or manually duplicate what the wizard can do.

Selecting the static page layout   Top of page

Selecting Static Page generates a simple, blank page. Your page starts with no data binding, no controls, and no special colors or backgrounds. Static pages are intended for content that will not change at runtime.

If you decide later to add data binding to your page without using the Page Wizard, you can do so with the Relational Data Palette. Choose the Data gallery icon to display the Relational Data Palette. Pick the top-level table from the dropdown list. Then, you can drag and drop database columns to the page.

Selecting from the data-bound page layouts   Top of page

Three of the Wizard layouts can bind the page to data. You can choose from a simple in-line text layout, a table layout, or an absolute positioning layout.

If you are new to HTML, you might find that it is easiest to use the absolute positioning layout. Experienced users of HTML might find the simple in-line text layout or table layout more suitable for some applications.

Selecting the HTML form page simple layout

Selecting HTML Form Page (simple layout) creates a dynamically generated page bound to a database table and places the controls in-line to the text flow. The Wizard adds a title, a label, and a control for each data column that you selected, plus buttons to navigate and edit the data.

Selecting the HTML form page table layout

Selecting HTML Form Page (table layout) creates a dynamically generated page bound to a database table and places the controls inside an HTML table. The wizard adds a title, a label and control for each data column that you selected, plus buttons to navigate and edit the data.

Selecting the HTML form page absolute positioning layout

Selecting HTML Form Page (absolute positioning) creates a dynamically generated page bound to a database table and places the controls inside an absolute positioning region. The Wizard adds a title, a label, and a control for each data column that you selected, plus buttons to navigate and edit the data.

The absolute positioning region aligns controls on a page. It allows you to position your controls easily on a two-dimensional canvas. You can also drop other HTML elements into it. In the browser, an absolute positioning layout gives a more precise appearance than a standard HTML layout.

SilverStream implements absolute positioning internally as an HTML table, which most browsers support.

You can create an absolute positioning region by:

To place another control anywhere in the absolute positioning region, first position the cursor in the desired location, then choose the toolbar icon for the control you want to create.

You can adjust properties for the absolute positioning region in the Property Inspector. You can adjust the size of the grid, specify whether controls will snap to it, specify whether it appears during design time, and adjust its alignment, size, and background color.

Selecting the Cell tab on the Property Inspector allows you to adjust the properties for a selected section of the region.

The Page Designer approximates the layout in an absolute positioning region. Factors that might affect the final appearance are whether the browser uses a different default font or different sizes for controls.

You cannot overlap controls in an absolute positioning region.

Creating a frameset   Top of page

When creating a new page, you also have the option of creating a frameset. A frameset does not contain text or image content. Instead, it consists of frames that display other pages.

Framesets commonly display pages with banners, navigation buttons, and menu choices in certain areas of the screen. Clicking on a choice in one frame typically brings up a new page of more detailed information in another frame. Netscape and MS Internet Explorer browsers versions 3.0 and above support frames.

The Wizard can automatically create these frame layouts:

You can add more frames by right-clicking a frame, then choosing to split it horizontally or vertically.

Adding data to pages   Top of page

Pages can have a primary data source as well as any number of additional data sources. An instance variable named agData provides access to the primary data source. The agData variable is available on every page, whether the page appears as a subpage or a stand-alone page. The agData variable is an instance of AgpDataPrimary, which is a subclass of AgpData.

You can bind controls to data at design time using the Property Inspector, or you can programmatically bind controls to data at runtime. You typically put binding code in the pageLoaded event.

You can also add as many additional data sources as you like by using the data control. It creates controls of the class AgpData. The AgpData class is similar to the AgcData class in SilverStream Java forms. These additional data sources can refer to other database tables or to Data Source Objects (DSOs). All AgpData objects have navigation methods for navigating within the database table, such as gotoNext(). They also have data manipulation methods for working with the data in each row, such as getProperty() and setProperty(). For working with the rows of the database table, they have row manipulation methods, such as insertAfter(). You obtain the contents of agData from the server and work with a copy of the database's contents.

    For detailed information on working with data, see Data Access Basics.

Validating user input   Top of page

Typically, you want to validate the data entered by the user before accepting it. You can either verify the data using the events provided in the Programming Editor or you can use JavaScript to check the data.

Using the validationTest and validationFailed Events

Certain controls fire the validationTest and validationFailed events, allowing you to verify the user's input. The server must receive a submit request before you can respond using these events, so feedback about entry errors might occur after the user made several changes. In the following example, the validationTest event for a text field requires the new entry to have at least two characters. The validationFailed event responds to the problem.

  // validationTest event for textField control 
statusArea.setText("ok");
String s = (String) evt.getNewValue();
if (s == null || s.length() < 2)
{
   throw new AgoValidationException(
   AgoValidationException.VALIDATION_FAIL);
}

// validationFailed event for textField control
statusArea.setText("Field must have at least two characters!");

The validationTest event only fires if the value in the control actually changed from its previous value.

Another set of events handles validation at a broader level. The globalValidationTest event fires just prior to some action that changes the whole page, such as navigating to the next record in the primary table. The globalValidationFailed event fires after any control on the page issues a validationFailed event.

Using JavaScript to validate data

You can use JavaScript to check values in controls on the page. Because JavaScript runs in the browser, this gives immediate feedback to the user and avoids a trip to the server. You could use the alert() method to pop up a message informing the user about the invalid data. The following example uses a text field's onBlur() JavaScript method to make sure the text field contains an entry.

  // store the typed text in a variable 
userNameEntry = this.userNameField.value;
if (userNameEntry == "")
{
   alert ("Please enter a name");
   return;
}

The onBlur() function occurs when the user leaves the focus of a control.

You could add validation to the submit button to make sure the user visited all of the entry fields.

    Also see Advanced Page Topics, for more information about adding JavaScript to a page.

Passing data between pages   Top of page

When constructing a multi-page application, you often need to pass data from one page to another page. This section provides some instructions and examples. One way is to encode the data in the URL for the new page by setting up a link expression. Another way is to store data on the session object.

Using a Link expression to another page

The Link expression provides a simple way to pass data to another page. The Link expression adds parameters to the URL when requesting the new page. URL parameters appear after a question mark. A parameter has a name, followed by an equals sign and the value of the parameter. The new page can then interpret these parameters during the pageRequestBegin event.

For example, you want to pass data to another page in a master/detail scenario. Perhaps a page shows a list of products inside a data view. When the user selects a product, you want to bring up another page with more information about that product. The page layout might look like this.

You would set the following properties for the label control that produces the list of products on the main page.

  1. Type text or an expression for the link in the Expression property. This text appears as a link on the page.

  2. Set the label's type to Expression Link. This treats the list items as links to another page rather than as plain text.

  3. Type the URL for the new page in the URL Expression property. Enclose the URL in double quotes. If you leave this field blank and the type is set to Expression Link, no data will appear in the data view.

  4. If you want the new page to appear in a frame, type the target frame in the Target Expression property.

  5. Add a new parameter for each piece of data you want to pass. Type the name of the parameter plus an expression for it. For example:

    Name

    Expression

    query

    "products.productid='" + products.productid + "'"

    The query in the previous example produces a link like this.

      productDetails.html?query=products.productid%3D'Micro EZ' 

NOTE   You do not have to use the percent sign to specify special characters in a link expression. For example, you can type an equals sign "=" rather than "%3D". SilverStream automatically transforms it for you.

SilverStream pages automatically handle two parameters. They are the query and order-by parameters. Thus, the new page that you link to will already know how to respond to the query parameter you pass to it. You can also create additional parameters. For parameters other than query and order-by, code the new page's pageRequestBegin event to interpret them. See the next section "Retrieving Parameters in the Destination Page" for more information.

The Page Designer makes it easy to pass data using a link expression, especially if you are familiar with URLs.

One limitation is that you can pass data only as strings. If you want to pass data as other types, pass the data using the session object.

Retrieving parameters in the destination page

You can retrieve the parameters passed to the new destination page in its pageRequestBegin event using the methods getParameterNames(), getParameterValues(), and getParameterAsObject(). Call these methods on the req object passed into the event. If you place your code in the pageGenerateBegin event, you must call getCurrentRequest() to get the req object. The following table describes these methods.

Method

Defined in class

Description

getParameterNames()

javax.servlet.
ServletRequest

Returns an Enumeration of all the parameters.

getParameterValues()

javax.servlet.
ServletRequest

Call it with the parameter name as a string. Always returns a string array. Access a single string in the first element.

Do not confuse this method with the getParameterValue() method.

getParameterAsObject()

AgiHttpServletRequest

Optimized alternative for retrieving single string values from the URL. You must test to see if it returns an array or a single string.

The following example retrieves the name of a city passed on a parameter named "Param1" using getParameterValues(), then displays it in a text field.

  String city[] = req.getParameterValues("Param1"); 
Field1.setText(city[0]);

The next example uses the getParameterAsObject() method and checks the return value to see if it is an array.

  AgiHttpServletRequest hreq = req; 
Object obj = hreq.getParameterAsObject("FirstName");
if (obj instanceof String)
{
   TextArea1.setText((String) obj);
}
else if (obj instanceof String[])
{
   // clear text area
   TextArea1.setText("");
   // go through array
   // cast Object to a String array
   String[] sArray = (String[])obj;
   for (int i=0; i<sArray.length; i++)
   {
      TextArea1.setText(TextArea1.getText() + sArray[i] + " ");
   }
}

Passing query and order-by to a page in a frameset

You can pass query and order-by parameters to a page inside a frameset with the setFrameLocation() method. This method requires using the instance variable agScriptHelper, so it requires JavaScript. One of the versions of setFrameLocation() lets you specify the page descriptor, the frame name, and the query and order-by strings as parameters. The following example references a frame named "left".

  setFrameLocation("page2.html", "left", "employees.name like 'A%'",    null); 

You can also pass parameters to a frameset by using the Target Expression property in the Property Inspector.

Passing data using the session object

You can pass data to another page by storing it on the session object. For example, you could save information about what the user selected and interpret it in another page. To store information on the session object, call setSessionValue() on the page using a name and a value. To retrieve the value later, call getSessionValue() using the name you previously gave to the value. For example:

  setSessionValue("cart", shoppingCart); 

and

  shoppingCart= (Vector) getSessionValue("cart"); 

Passing data using the session object offers more flexibility than a link expression because you can pass other data types in addition to strings. Also, the URL length restrictions do not limit the data you can pass on the session object. Both methods have comparable performance.

The browser uses the host name for the calling page to determine which cookies to send to the server. The server then uses the session cookie to identify the session. If the browser determines that the host name for a link is different from the host name associated with the calling page, it doesn't send a cookie and the server establishes a new session. Since a single server can have multiple host names, you need to be sure to use the host name associated with the calling page for each subsequent page request so that you can access the correct session.

To ensure that you use the correct host name for links and redirections, you can use relative paths to reference pages. If you need to specify the host name programmatically, you can use the getServerName() method on the ServletRequest class or the getRequestURL() method on the HttpUtils class to get the correct name. These methods always return the host name associated with the calling page. Do not use the getDatabaseURL() method on the AgpPage class because this method returns the official name of the host, which may be different from the host name for the calling page.

Working with page controls   Top of page

This section describes a number of techniques you can use to program the controls on a page.

Setting page control properties   Top of page

Properties are attributes that you can set at design time for a particular Designer object or control. You specify property values in the Property Inspector dialog.

Properties are part of the visual programming toolset that SilverStream provides. Properties are not part of the SilverStream API. The properties you specify for a particular page control turn into metadata that tells SilverStream how to generate that control. Often this results in method calls to a corresponding class in the API, but not always.

Because properties are not part of the API, you cannot access them directly at runtime like instance variables. But for many properties, the API does provide accessor (get and set) methods that enable you to modify these property values. For example, to disable HTML generation for a label control, you could call the setEnableHTMLGeneration() method:

  Label1.setEnableHTMLGeneration(false); 

The API also provides the getProperty() and setProperty() methods to allow you to modify HTML attributes directly. Any page control that inherits from AgpTag supports these methods. For example, to set the background image for a table, you could specify a value for the BACKGROUND attribute by calling the setProperty() method:

  mytable.setProperty("BACKGROUND", 
   "/SilverStream/Objectstore/Images/Clouds.jpg");

Making objects programmable   Top of page

Many of the built-in controls on a page are by definition programmable. For example, text fields, text areas, labels, buttons, checkboxes, radio buttons, choice and list boxes are automatically available in the Programming Editor as callable objects. These objects have methods you can call and events that fire in response to user actions.

Some controls on a page are not programmable by default, but can optionally be made programmable. You can make an optionally programmable control become programmable by giving it a name, then checking the Programmable checkbox on the Property Inspector.

Optionally programmable controls include:

NOTE   To make a table cell programmable, you need to make the containing table programmable as well.

In addition, regular text and paragraphs can be made into programmable controls.

Once you make a control programmable, it appears in the Programming Editor as a callable object. If it fires events, it also appears in the Controls dropdown list. You can call methods on it, change its properties, and respond to available events.

The Name property in the Property Inspector also doubles as the HTML ID or NAME property for some elements. This allows JavaScript and style sheets to change the control's properties.

Setting text in label controls   Top of page

You have two options for setting the text that a label control displays.

You cannot combine these two techniques. If you set the text in the Property Inspector at design time, that setting overrides any other value. To set the text dynamically at runtime, leave the Expression property blank.

You can use the Label tag to include raw HTML in a page. For example, suppose you want to wrap some text in the HTML emphasis tag at run time. To do this, you would first need to call the setRawHTMLMode() method with a parameter of true. Then you could call the setText() method to specify the raw HTML for the label:

  String mylabel = Label1.getText(); 
Label1.setRawHTMLMode(true);
Label1.setText("<EM>" + mylabel + "</EM>");

Specifying data typing and formatting for text fields   Top of page

SilverStream allows you to assign a data type to and format the display of text field controls on a page. You can specify these characteristics of a text field at design time in the Property Inspector or programmatically at run time.

NOTE   Text fields on pages you create in SilverStream can also specify the HTML type attribute. Do not confuse the data type of a field with the HTML type for the field. The HTML type attribute allows you to indicate whether the field is Text, Hidden, or Password. To specify the type attribute for a text field, you select one of these options in the Type control in the Property Inspector.

Text fields in a page can have the following data types:

At design time, you specify the data type for a text field in the Data Type control in the Property Inspector.

You can specify display formatting for all data types except String. To tell SilverStream how to format a text field, you can select from a predefined set of named display styles or create your own display pattern. At design time, you specify display formatting for a text field in the Display Format control in the Property Inspector.

The AgpTextField class has several get and set methods that allow you to specify the data type and display characteristics of a text field programmatically. AgpTextField also provides methods for getting and setting text field values based on the data type specified for the field:

Method(s)

Description

getDatatype()/setDatatype()

Gets or sets the data type for a text field

getDisplayPattern()/setDisplayPattern()

Gets or sets a user-defined display pattern

getDisplayStyle()/setDisplayStyle()

Gets or sets a predefined display style. If you specify a display pattern by calling setDisplayPattern(), this pattern overrides the display style.

getIntegerValue()/setIntegerValue()

getDoubleValue()/setDoubleValue()

getBigDecimalValue()/setBigDecimalValue()

getDateValue()/setDateValue()

getTimeValue()/setTimeValue()

getTimestampValue()/setTimestampValue()

Gets or sets a text field value using the data type specified for the field

getText()/setText()

Gets or sets a text field value using the String data type

For example, if you added a text field called Field1 to a page and specified that the data type for the field were Integer, you could modify the field's display style to use the currency style by calling the setDisplayStyle() method. To specify the display style, you would use a constant defined on the AgFormat class:

  Field1.setDisplayStyle(AgFormat.CURRENCYSTYLE); 

Alternatively, you could modify the formatting by specifying a display pattern. To do this, you would call the setDisplayPattern() method:

  Field1.setDisplayPattern("$#,##0.00;($#,##0.00)"); 

The structure of the pattern must conform to the rules for the specified data type. For Integer and floating point fields, see the java.text.DecimalFormat documentation. For Time, Date, and Timestamp fields, see the java.text.SimpleDateFormat documentation.

Using HTML data controls   Top of page

The HTML data control embeds HTML pages stored in a database into a dynamically generated page. It could display the HTML data authored using the HTML editing control in a SilverStream Java form. Like other input controls, the HTML data control supports data binding.

The HTML data control is similar to a dynamically generated subpage. It processes the HTML just like a page does, so you can have interactive elements in the HTML. Also, you can dynamically modify the properties of the elements in the HTML data, just as you can on a page.

Compared to the label control, the HTML data control is more powerful because it processes the HTML as a page. Use the label control to generate a snippet of HTML, and use the HTML data control for embedding content pages.

Unlike the HTML editing control in the Form Designer, the HTML data control in the Page Designer can only display HTML. It does not support editing.

NOTE   You must properly form the HTML data in an HTML data control as a full HTML page. Generation of the control's contents will fail if it contains invalid HTML syntax.

Adding images   Top of page

Images enhance the appearance of your pages. Some ways to use images are:

Dynamically displaying images in data views

To show data-bound images in a data view, insert a label control rather than an image control.

  1. Set the label control to Raw HTML mode in the pageLoaded event by calling the setRawHTMLMode() method with a parameter of true.

  2. In the label's Expression property, construct an expression that will create an HTML image tag at runtime, in a format similar to this:

      "<img src=\"../Objectstore/Images/" + tblOffices.imageFile  
       + ".gif\">"

    Include data from the database in the expression to locate the image files at runtime.

You might want to use images in a data view to display pictures of products, to keep employee photos on file, or to show locations on a map.

Displaying data-bound images elsewhere on a page

You can display dynamic data-bound images outside of a data view.

  1. Create an image control on a data-bound page.

  2. Make the image control programmable by giving it a name.

  3. Check the Programmable checkbox in the Property Inspector.

  4. In the pageGenerateBegin event for the page, write code that depends on data, using the setSrc() method. The following example sets the image source to filenames stored in a database.

      agpData1.refreshRows(); 
    String filename = (String) agpData1.getProperty("FileName");
    Image1.setSrc(filename);

Generating dynamic images not bound to data

To dynamically change an image that does not depend on a database table, you can create an image control and make it programmable. In the pageGenerateBegin event, write code using the setSrc() method to change the image.

  Object obj; 
obj = getSessionValue("buyList");
if (obj == null)
   Image1.setSrc("nullGraphic.gif");
else
   Image1.setSrc("purchaseGraphic.gif");

Creating links to e-mail   Top of page

You can easily create a link for the user to send e-mail. When the user clicks on this link, an e-mail composition window appears with an address already filled in. To add an e-mail link to your page, follow these steps.

  1. Type the text you want to appear in the page.

  2. Highlight the text that will be part of the link.

  3. Bring up the Property Inspector and select the Text tab.

  4. In the Page property under the Link section, type mailto: followed by the e-mail address. For example:

      mailto:info@silverstream.com 

Alternatively, you could create a label, set its type to Expression Link, and set its URL Expression property to the "mailto:" string.

Including Java forms and views in a page   Top of page

You can place any Java form or Java view (created with the Form Designer or View Designer) on a page. These forms and views contain Java code that runs on the user's machine, whereas the Java code you write in the Page Designer's Programming Editor runs on the server.

NOTE   Including forms and views in pages is not supported in SilverStream Version 3.0 due to limitations of the Sun Java 1.2 browser plug-in. We hope to re-enable this support in SilverStream in the future. See the release notes for updated information.

When your page contains both Java controls (forms and views) and HTML controls, a request to the server resets the data in the Java controls. SilverStream recommends that you have either Java controls on a page or HTML controls on a page, but not both.

You can set properties on Java forms and views in the Property Inspector. You can also make them programmable and set their properties at runtime. After making a presentation programmable, you can pass data to it from the page. The page can read parameters passed on the URL, then set parameters on the presentation.

If you choose to include Java forms or views on your page, the user needs a Java-enabled browser to run the page. In addition, the user must initially wait for the Java classes to download. These requirements could limit the size of your user base. You should choose this option only when you know that your users have Java-enabled browsers and that they are willing to wait a little longer when they visit your page for the first time in a session. For most applications that use a browser on the client machine, you will want to build pages that execute HTML and JavaScript on the client, but not Java.

SilverStream 3.0 supports the Java 1.2 Plug-in provided by Sun. The Java 1.2 Plug-in lets you run applets inside a browser using the Java 2 Runtime Environment, instead of the browser's default virtual machine. This lets you take full advantage of the features of the Java 2 SDK. When you add a Java form or view to a page in SilverStream, you can specify whether you want to use the Java 1.2 Plug-in. The Java 1.2 Plug-In is required to run forms and views in a browser unless the browser has an embedded 1.2 Java virtual machine.

To use the Java 1.2 Plug-In to run SilverStream forms and views, you need to have Internet Explorer 4 or higher. Applets that are not SilverStream forms should work in whatever browser they are written for. You can use the Java 1.2 Plug-In for these kinds of applets, but you do not need to.

Here's what happens at runtime when you use the Java 1.2 Plug-In:

When testing pages that contain Java forms or views, you need to use a batch file to start your browser. The batch file must set the CLASSPATH and start the browser program. You can simply clear the CLASSPATH (as shown below) or specify path information that suits your application requirements:

  set classpath= 
start iexplore %1 %2 %3 %4 %5

To tell SilverStream where to find the batch file, select Edit>Preferences in the SilverStream Designer and specify the batch file in the Browser field on the General tab of the Preferences dialog.

Working with subpages   Top of page

You can place pages inside other pages, in which case they become subpages. Subpages provide an easy way to reuse common elements among pages. Reusing elements saves significant time when setting up and maintaining complex Web sites. Subpages also provide an excellent alternative to framesets, which can sometimes take longer to load and look different depending on the type of browser used.

Subpages might contain:

SilverStream employs a dynamic name assignment model that resolves any conflicts if subpages contain controls that have the same name as controls on the parent page.

NOTE   If you use style sheets within subpages, you must follow the SilverStream dynamic naming model, since style sheets are not dynamically renamed. SilverStream recommends that you define style sheets at the topmost page level.

Calling subpage methods from the parent page   Top of page

To pass information between the parent page and a subpage, you can call the subpage's methods from the parent page. You do not need to cast the subpage in order to call one of its methods because the data type for the subpage on the parent is the class for the subpage. For example, suppose you drop a subpage named "shoppingCart" onto a page named "mall". On the parent page, the subpage is named "subpage1". In this case, you can call a method on the subpage by referencing the instance variable on the parent page.

  subpage1.addPurchase(anItem, aPrice); 

Calling parent page methods from the subpage   Top of page

In a subpage, you can call a method that is defined on the parent page. To call a method on the parent page, you first need to call getParentPage() to get a reference to the parent page.

  boolean bFixup = getParentPage().getEnableHistoryFixup(); 

If the method you want to call is a user-defined method on the parent page, you need to first cast the object reference returned by getParentPage():

  myparentpage objref = (myparentpage) getParentPage(); 
String mystring = objref.mymethod();

Broadcasting events between subpages   Top of page

You can broadcast events from one subpage to another. You might want to do this if a subpage needs to perform special processing (for example, changing its display) whenever the user performs an action on another subpage. To broadcast events between subpages, you need to use an event listener model:

Suppose you have a set of pages that allows the user to browse a list of products. The parent page contains two subpages that allow the user to navigate through the data. The user can view a list of products in one subpage and click on a particular product to see product details in another subpage. This architecture has several components:

    The code for each of these components is available in the SilverStream help. See Application Techniques>HTML Client Techniques in the help system: Broadcasting Events between Subpages.

Navigating between pages   Top of page

You can bring up other pages in the same window or new browser windows in a variety of ways. The page provides a showPage() method. The instance variable agScriptHelper supports JavaScript functionality for opening and closing windows, and you can also call JavaScript functions directly.

The table below describes several methods you can use to navigate between pages. All of the methods, except for showPage(), require the user to have a JavaScript-enabled browser.

Method or Function

Called

Description

showPage()

On the page

Redirects the most recent request for the page to a different URL. You can also pass a query parameter, order-by parameter, and hashtable.

If you add a cookie to the response for a page, and then call showPage(), the cookie will not be sent to the browser.

NOTE   This was not the case in SilverStream 2.5 because the response headers were not reset when you redirected the request to a different URL.

openWindow()

On agScriptHelper

Brings up the specified page. You can set window features and pass a query parameter, order-by parameter, and hashtable.

closeWindow()

On agScriptHelper

Closes the current browser window. Use only for additionally opened windows.

setFrameLocation()

On agScriptHelper

Moves the specified frame of the browser to the given URL with the parameters specified.

reloadFrame()

On agScriptHelper

Reloads the specified frame.

reloadTopFrame()

On agScriptHelper

Reloads the top-level frame.

window.open()

In JavaScript

Opens a window with the given specifications.

close()

In JavaScript

Closes the referenced window.

The following example uses openWindow() to open a window with a specified dimension, with tool buttons turned off, the menubar turned on, and the status bar turned off.

  agScriptHelper.openWindow("http://www.silverstream.com/", 
   "SilverStream","toolbar=false;menubar=true;status=off;
   width=300;height=400;");

This causes the browser to open an additional window and bring up the SilverStream Web site.

You might want to open another browser window to get information from the user, as you would with a regular dialog. Just before closing the page that is functioning as a dialog, refresh the main page with any changed data. The following example refreshes the main page and uses some JavaScript functions.

  // pageActionPerformed event of the Save or OK button 
// on the dialog
agData.updateRows(); // saves the changes to the database
// JavaScript code
// Tell the frame that opened this window to refresh,
// as the data has changed.
writeScript("window.opener.location=
   \"CustomerStatement.html?query=Customers.customerid%3d'"
   +customerid.getValue()+"'\"");
// Now close this window
writeScript("self.close()");

Using an invisible tab control to create a multi-page look   Top of page

When you use multiple pages to display sequential screens in an application, you must handle page state. This requires extra coding. In addition, it can be expensive to load data on each page. An alternative approach is to use an invisible tab control on a page. For example, you could use an invisible tab control to simulate a wizard. In this case, you could add Next and Back buttons outside of the tab control to switch between the panes of the tab control.

To make a tab control invisible at runtime, you need to uncheck the Show tabs at runtime checkbox in the Property Inspector for the tab control. This makes the tabs disappear, so you need to provide a way to navigate between the panes. You can do this programmatically.

To provide a way to navigate between the panes:

  1. Name the tab control.

  2. Mark it as Programmable.

  3. In your navigation controls, call the setCurrentPane() method on the tab control, passing an integer value that indicates which pane should be displayed.

    The pane numbers are zero-based. Therefore, to access the first pane, you need to specify 0 as the integer value.

    An example of this technique is provided in the SilverStream help. See Application Techniques>HTML Client Techniques in the help system: Using an Invisible Tab Control to Create a Multi-Page Look.

Invoking a business object   Top of page

Pages can interact with business objects and pass information to them. Business objects are able to remember information between sessions. For example, you could implement a chat room with the business object brokering the sessions.

To invoke a business object, call invokeBusinessObject() on the page and optionally include a second parameter to pass to the business object. For example:

  Object result = invokeBusinessObject("com.xyz.myBusObj", 
   "A String param");

The first parameter represents the name of the business object (including the package). This assumes that the business object resides in the same database as the page. To call a business object in a different database on the same server, add the name of the database and a colon to the beginning of the name.

The method returns a result set as defined by the business object.

Handling page exceptions   Top of page

The page object will automatically catch exceptions that occur on pages. Pages support an event called handlePageException that allows you to customize exception handling. You can write code for this event to test the type of exception, provide custom handling, and return true if your code handled the exception. Otherwise, the server will create a response page to notify the user of the exception.

Writing custom methods   Top of page

You can create your own custom method by selecting Tools>Add New Method in the Programming Editor. The Wizard helps you create the new method. The new method appears under the General section.

Adding helper classes   Top of page

You can define helper classes using the Business Object Designer. See the chapter on business objects for more information.

Customizing pages for users    Top of page

Some types of applications customize the user interface based on the individual user or the user's group. For example, an application might display employee information to all employees and allow managers to update the information. The application can hide the update button from users that aren't supposed to make updates.

You can set up your server to require user login or you can specify that individual objects require login before the user can open them. You use the SilverStream Management Console to add users, assign them to groups, and set permissions for individual objects. To find out how, see the information on users and groups in the Administrator's Guide.

When a page requires login, you can get the user's login name and set up the page according to that user's needs. There are two methods in the AgpPage class for getting information about the current user:

After you get the user's login name or group membership, you can programmatically enable and disable controls on the page; or you can make certain controls visible, invisible, or read-only depending on the control.

NOTE   Customizing the user interface to disable functionality does not constitute security for your server. You must also take appropriate steps to make your server secure. For information, see information on security in the Administrator's Guide.

Example

The following code illustrates how you might enable a control based on the user's group membership. The example tests whether the user is part of a group named "Employees". If the user is in the group, an AgpTextArea control containing a product description is made editable so the employee can maintain the description.

  if (userInGroup("Employees")) 
   TextArea1.setEnableHTMLGeneration(true);
else
   TextArea1.setEnableHTMLGeneration(false);






Copyright © 2000, SilverStream Software, Inc. All rights reserved.