Sybernet SDK

Introduction

This document attempts to describe the implementation of a Style Guide for Sybase stored procedures written for the World Wide Web. This is not a concrete defacto standard and any comments or changes are welcome.

We will begin this discussion with a description of four stored procedures that allow you to create hyper-text links (anchor objects), forms, form elements, and image-buttons (real images that behave like buttons to submit a form). These procedures reside in SYBSYSTEMPROCS which makes them global to every database that references them. You do not need to qualify the database name when invoking them. In fact it is probably a good idea not to since the database context will change if you do.

The next section will describe how these procedures are put together and used in a real world example. It uses the background.gif image that contains a blue margin bar on the left with a white background on the right. A template that uses this background image is explained before we actually start adding buttons and fields.

The Procedures

These procedures will allow you to invoke JavaScript functions (like alert and confirm) in a easy to use fashion while also allowing you provide your own functions without sacrificing syntax. Using these procedures you no longer need to worry about making sure you've quoted each name and value pair correctly, nor do you need to worry about whether a result set might result in truncation if you concatenate too many strings.

If you are already familar with writing HTML (in particular forms and links), you should find it fairly easy to call a stored procedure to accomplish the same thing.

Input fields separate each item with NAME=VALUE pairs; for example, you may already be used to specifying something like this:

    select '<INPUT TYPE="TEXT" NAME="ADDRESS" VALUE="' + @ADDRESS + '">'
With these procedures the NAME part is a parameter name and the VALUE part is the value for that parameter. Its execution resembles the following:

    exec sp_html_input @TYPE="TEXT", @NAME="ADDRESS", @VALUE=@ADDRESS

Why an SDK?

For one thing, the Sybernet SDK can eliminate several common errors when writing HTML in a stored procedure. The example above illustrates this well because there could be a problem with this select statement if the result of that string exceeds 255 characters: Sybase will truncate it and our form will be incorrect.

By using the SDK you can also eliminate a lot of redundant coding. The RADIO and CHECKBOX fields illustrate this nicely when you want to indicate whether or not the field is CHECKED. To do this in a stored procedure you would probably compare two strings for equality and append the CHECKED option when they are equal. The SDK will do this for you automatically.

The SDK is also JavaScript aware. With a single string parameter you can warn your users that clicking a particular button will cause a record to be deleted. This is convenient both for the user because the feed-back is immediate and for you because you do not need to handle a similar warning in your stored procedure.

The SDK also recognizes many of the standard Sybase datatypes such as INT and NUMERIC and will automatically validate that values entered in these fields are within an acceptable range; for example, a TINYINT must be a whole number between 0 and 255. Without this validation your stored procedure might generate an exception condition when that parameter was converted. And again, the validation occurs on the client side so the feed-back is immediate.

What's new in Version 2.22?

Version 2.22 of the Sybernet SDK moved all inline JavaScript functions to a separate stored procedure that is invoked when you call sp_html_form. As inline functions, versions prior to 2.22 attempted to keep this code as compact as possible. Now that they are independent functions and not inline code, several new features not otherwise possible have been included.

Though I haven't seen any harm as of yet, you will want to review the section on sp_html_form so that these functions are not included on multiple invocations of same, if for no other reason than to reduce the bandwith.

Version 2.22 also introduced the PICTURE tag. This feature is now used by the SDK to format numeric data when it is displayed on your browser. Numeric data may now be right-aligned (ALIGN), scaled to the correct number of decimal digits (SCALE), and displayed with commas (COMMAS) at the proper location.

MONEY, SMALLMONEY and DECIMAL were also added to the Sybernet SDK in version 2.22. In addition, you can now pass numeric data containing commas and they will be stripped and reedited before they are displayed on your form.


sp_html_form

sp_html_form is a stored procedure which creates the beginning and ending FORM tag. The ending form tag </FORM> is created when all of the parameters to this procedure are null. The beginning form tag is created when any of the parameters are non-null. The parameters to this procedure are as follows:

Examples

The first example creates the beginning Form tag. The form NAME is specified because I am going to reference this form later when I create an image button that submits this form when it is clicked.

       exec sp_html_form

           @NAME = "myForm"
       ,   @METHOD = "post"
       ,   @ACTION = "Sybernet.cgi"
       ,   @PROCNAME = "sp_html_template"
       ,   @FORMAT = "SUPPRESSED"
Since METHOD is optional and ACTION is ignored, the minimum needed to create the beginning form tag (assuming the form is not named) would be accomplished with the following:
       exec sp_html_form @PROCNAME = "sp_html_template"
The next example simply creates the ending Form tag. The ending tag is created because no parameters were passed to this procedure.

       exec sp_html_form
If you use the SDK to create more than one input form, then you will want to inhibit the inclusion of the JavaScript functions that validate your form fields.
        exec sp_html_form

        ,   @METHOD = "POST"
        ,   @JAVASCRIPT = "FALSE"
Failure to do so will include these functions for each invocation; however, although this does not create a problem for JavaScript, it does increase the size of your HTML screen.


sp_html_input

sp_html_input creates an input field. All of the input fields that you are familar with may be created with this procedure. You can also create a TEXTAREA field with this procedure, but as you might expect its value part may not be an image or text field.

ALERT and CONFIRM are special names that are not normal items in this tag. ALERT and CONFIRM are automatically passed to the onClick event handler to display a JavaScript alert and confirm, respectively.

All of the parameters are optional just like in regular HTML . All parameters are varchars unless otherwise indicated.

Examples

The first example is a simple input field. There are two things to note about this example: the procedure will automtically quote the @ADDRESS value for you, and this value will not be concatenated to any other string because that might result in truncation.

       exec sp_html_input

           @TYPE = "text"
       ,   @NAME = "address"
       ,   @VALUE = @ADDRESS
       ,   @SIZE = "30"
Here are two radio buttons. sp_html_input will automatically determine if the button is checked based on the parameter name (@PIZZA). This is actually very convenient because to do this yourself you would have to test whether or not @PIZZA was equal to each button value.

       exec sp_html_input

           @TYPE = "radio"
       ,   @NAME = "PIZZA"
       ,   @VALUE = "Cheese"
       ,   @CHECKED = @PIZZA
	   
       exec sp_html_input

           @TYPE = "radio"
       ,   @NAME = "PIZZA"
       ,   @VALUE = "Pepperoni"
       ,   @CHECKED = @PIZZA
The next example illustrates how you can use Sybase datatypes when calling sp_html_input.

        exec sp_html_input

           @TYPE = "SMALLINT"
       ,   @NAME = "Balance"
Because they were not specified, the Sybernet SDK will automatically set both SIZE and MAXLENGTH to 6. It will also validate that the value in this field is a whole number between -32,768 and 32,767. Here's another example that overrides these defaults:

        exec sp_html_input

           @TYPE = "INT"
       ,   @NAME = "Account"
       ,   @SIZE = "30"
       ,   @MAXLENGTH = "30"
       ,   @minValue = "200000"
       ,   @maxValue = "299999"
The next example specifies the Sybase datetype DATETIME and validates that the date entered is at least November 25, 1996.

        exec sp_html_input

           @TYPE = "DATETIME"
       ,   @NAME = "Birthday"
       ,   @minValue = "Nov 25, 1996"
A standard submit button which displays a confirmation before submitting the form. The button is named because the procedure that receives this form wants to know which button the user clicked.

       exec sp_html_input

           @TYPE = "submit"
       ,   @NAME = "button"
       ,   @VALUE = "Save"
       ,   @CONFIRM = "Are you sure you want to save this form?"
Similar to the example above except an alert is displayed and the form is not submitted. Normally, this would be preceded by some condition that tests whether or not the user is allowed to click this button.

       exec sp_html_input

           @TYPE = "submit"
       ,   @NAME = "button"
       ,   @VALUE = "Drop"
       ,   @ALERT = "You are not permitted to drop this form!"
This is just a simple button that behaves exactly like the BACK button on your browser's tool bar. Notice that it uses the onClick event handler and supplies the entire JavaScript code in the onClick parameter.

       exec sp_html_input

           @TYPE = "button"
       ,   @VALUE = "Back"
       ,   @onClick = "window.history.back()"
Version 2.22 of the Sybernet SDK introduced 3 new parameters that allow you to control how numeric data is displayed and edited by the user. They are ALIGN, SCALE and COMMA.

ALIGN may be either TRUE, FALSE or NULL. When TRUE, numeric data is right-aligned. SCALE is a positive integer that determines the number of decimal digits that are displayed and edited. If zero, no decimal digits are displayed. COMMAS may be either TRUE, FALSE or NULL. When TRUE, commas are inserted at the expected location.

       exec sp_html_input

           @TYPE = "NUMERIC"
       ,   @VALUE = "1,234.5678"
       ,   @ALIGN = "TRUE"
       ,   @SCALE = "2"
       ,   @COMMAS = "TRUE"
In the above example, the value is right-aligned, scaled to two decimal digits, and commas are inserted at the proper position. Notice too that the value contains commas; these are removed and then reinserted as expected.


sp_html_button

sp_html_button creates a button suitable for submitting a form. Unlike regular submit buttons that you are already familiar with and which occur inside of the <FORM> tag, the buttons created by this procedure can be real images. They can also be regular submit buttons as well, and the reason for doing this will be explained shortly.

An image that acts like a submit button is accomplished by the JavaScript submit() and reset() functions. This means that a button or image built by sp_html_button must be able to refer to the form being submitted. This is the NAME that is given to sp_html_form when you define that form. NAME is passed as FORM when calling this procedure.

WARNING: If you attempt to call this procedure in the middle of a form (between the <FORM> and </FORM> tags), results will probably not be correct. This is especially true if you are creating "regular" submit buttons (as opposed to image buttons) or you are using the PROMPT option of this procedure. Either declare your buttons normally using sp_html_input or place these buttons outside of your form.

WARNING: This procedure assumes that you are using FORMAT=SUPPRESSED. If you are not using this format, some of the buttons will not work or cause conversion errors in Sybase when your form is submitted.

All of the parameters are varchars. Only TYPE is required, but if that is the only parameter passed to this procedure, you are probably doing something wrong.

Examples

We first need to define a form screen. The form screen is required, but since the form is referenced by its name, you can actually create your buttons before creating the form. For our purposes, however, it will be clearer if we define the form first and then show how the procedure is used.

Since our buttons are no longer part of our form, we want to declare a hidden field that will be used to store the value of our button. In the following form declaration, the name of this button will be called BUTTON.

       exec sp_html_form

           @NAME = "myForm"
       ,   @PROCNAME = "sp_html_form"
       ,   @FORMAT = "SUPPRESSED"
	   
       exec sp_html_input

           @TYPE="HIDDEN"
       ,   @NAME="BUTTON"

       exec sp_html_input

           @TYPE="HIDDEN"
       ,   @NAME="SEARCH"

       exec sp_html_form
The important parts are displayed in bold. NAME is going to be used to reference our form, BUTTON is going to be the value that is set when our form is submitted, and SEARCH will be assigned the text entered by your user when the prompt option is used.

The first example illustrates how to define a reset button:

       exec sp_html_button

           @TYPE="RESET"
       ,   @NAME="BUTTON"
       ,   @VALUE="Reset"
       ,   @FORM="myForm"
Since IMAGE was not defined and TYPE is not one of our keywords, this is just a simple reset button.

While this looks like a regular reset button, it can actually reside in a different window or frame. If your buttons do not, you should probably define your submit buttons with sp_html_input.

Here is how to define the same button as an image:

       exec sp_html_button

           @TYPE="UNDO"
       ,   @NAME="BUTTON"
       ,   @VALUE="Reset"
       ,   @FORM="myForm"
       ,   @STATUS="Reset information on this page"
Since UNDO is one of our keywords, IMAGE and TYPE are automatically assigned.

The next example illustrates how to define a submit button:

       exec sp_html_button

           @TYPE="SAVE"
       ,   @NAME="BUTTON"
       ,   @VALUE="Save"
       ,   @FORM="myForm"
       ,   @STATUS="Click here to save your changes"
When this image button is clicked, the form "myForm" is submitted and the value of @BUTTON is set to "Save."

Here's a similar example that first asks the user if they really want to save their change and then calls a JavaScript function (defined by you) before the form is submitted:

       exec sp_html_button

           @TYPE="SAVE"
       ,   @NAME="BUTTON"
       ,   @VALUE="Save"
       ,   @FORM="myForm"
       ,   @CONFIRM="You really want to save this?"
       ,   @onClick="if (!validate(myForm)) return false;"
       ,   @STATUS="Click here to save your changes"
In order, the user is first asked to confirm that that they really want to save this form. If that succeeds, your JavaScript function validate is called. If that succeeds, only then is the form submitted.

The next example illustrates how to override the form submission and call another procedure:

       exec sp_html_button

           @TYPE="HOME"
       ,   @PROCNAME="sp_html_login"
       ,   @TARGET="_parent"
       ,   @FORM="Home"
       ,   @NAME="Home"
       ,   @VALUE="Home"
       ,   @STATUS="Return to the Sybernet main menu"
Since a procedure name is specified for PROCNAME, the values for FORM, NAME, and VALUE are not important. When this button or image is clicked, TARGET is set to "_parent" and the procedure "sp_html_login" is invoked.

The next example illustrates how you can prompt your user for some text. It uses the PROMPT option, and only the value of your button (indicated by NAME) and the text that was entered when the prompt was displayed is returned to your procedure:

       exec sp_html_button

           @TYPE="FIND"
       ,   @PROCNAME="sp_html_form"
       ,   @TARGET="Frame2"
       ,   @NAME="Button"
       ,   @VALUE="Find"
       ,   @STATUS="Search for some text in this file"
       ,   @PROMPT = "SEARCH"
Since the PROMPT option does not invoke our form, it is important to supply the name of the procedure you wish to execute. The name of the form is not needed; in fact, it is ignored. TARGET should be specified if you are using frames so that the results of the find can appear in their own window. PROMPT also defines the name on your form (and is probably hidden) that is to receive the user's response. Note too that the value entered is "remembered" if your buttons appear in their own window or frame; otherwise, the value is reset whenever a form is submitted and the buttons are redrawn.

If you create more than one button with this procedure, you should probably include the whole thing in a table so that the buttons are exactly where you want them. Here is an example of how you might create a tool bar:

       select "<TABLE BORDER=4 BGCOLOR=BLACK><TR><TD ALIGN=CENTER>"
       select "<TABLE BORDER=0 WIDTH=100% BGCOLOR=BLACK CELLPADDING=0 CELLSPACING=0><TR><TD>"

       exec sp_html_button @TYPE="SAVE", @NAME="BUTTON", @FORM="MyForm", @VALUE="Save"
       exec sp_html_button @TYPE="NEXT", @NAME="BUTTON", @FORM="MyForm", @VALUE="Next"
       exec sp_html_button @TYPE="LAST", @NAME="BUTTON", @FORM="MyForm", @VALUE="Last"

       select "</TD></TR></TABLE>"
       select "</TD></TR></TABLE>"

sp_html_href

sp_html_href creates an anchor tag and is specifically designed to make calling stored procedures and passing parameters to those procedures as easy as possible.

To add the ability to create a directory of hyper-text links, we've added some parameters to do this. In particular, DESCRIPTION will cause the anchor and description to be separated by paragraph tags.

All of the parameters are optional (which doesn't make a lot of sense, but it is consistent with HTML) and they are all varchars.

Examples

The first example is a simple anchor object that calls the main menu screen.

       exec sp_html_href

           @PROCNAME = "sp_html_login"
      ,    @anchorText = "Return to the Sybernet Main Menu screen"
      ,    @STATUS = "Click here to return to the main menu"
The next example constructs a hyper-text link that will invoke the procedure sp_html_template with a single (unnamed) parameter of "Order Pizza." That parameter is received by this procedure in the variable @SEARCH, not to be confused with the @SEARCH parameter that we set when we call sp_html_href.

       exec sp_html_href

           @PROCNAME = "sp_html_template"
      ,    @SEARCH = "Order Pizza"
      ,    @CHECKED = @SEARCH
      ,    @DESCRIPTION = "Click here to order your pizza on the world wide web."
It is also worth noting that the anchorText will default to the value for @SEARCH and @STATUS will default to @DESCRIPTION since neither of these were specified.

Since the name of the Sybernet CGI is automatically included when PROCNAME is not NULL, you wouldn't use this procedure to create anchor tags that point to anything other than a Sybase stored procedure; however, JavaScript is quite powerful, and this can be accomplished by the following example:

       exec sp_html_href

           @PROCNAME = NULL
      ,    @anchorText = "Sybernet Home Page"
      ,    @onClick = "this.href='http://Sybernet.sri.com/'"
Normally, this trick is reserved for those cases where the href needs to be determined dynamcially. Or it might be used to open a second window:

       exec sp_html_href

           @PROCNAME = NULL
      ,    @anchorText = "Open Another Window"
      ,    @onClick = "window.open('','Sybernet','width=600,height=160');this.href='http://Sybernet.sri.com/'"
      ,    @TARGET = "Sybernet"

http..sp_html_menubar

You can override the default Instructions when calling the menubar by specifying the name of the stored procedure that will provide instructions.

The parameters allowed to this procedure include the following:

Examples

The first example is how you would direct output of your help procedure to the same window.

        exec http..sp_html_menubar

            @PROCNAME = "sp_html_help"
        ,   @SEARCH = "Step 3"
        ,   @STATUS = "Click here for information on how to use this tool!"
Notice that the parameter "Step 3" is passed to "sp_html_help."

The next example works exactly the same way except that it creates a new browser window to send this information.

        exec http..sp_html_menubar

            @PROCNAME = "sp_html_help"
        ,   @SEARCH = "Step 3"
        ,   @TARGET = "HelpWindow"
        ,   @WIDTH = "400"
        ,   @HEIGHT = "600"

sp_audit

sp_audit is used to track the execution of stored procedures. Your web server's log file can also track this information, but the data is hard to get at and the web server cannot track form requests.

The prototype for sp_audit looks like this:

    create procedure sp_audit
    (
        @PROCNAME          varchar(92)             -- Name of procedure.
    )
The audit file that this information is written to contains the following columns: sp_audit can of course be called anytime you desire. sp_html_frame automatically calls this procedure whenever BUTTON is NULL.

Example

The following example calls sp_getProcname to retrieve your procedure name at run-time. It then calls sp_audit and passes that name as the only parameter.

    select @PROCID = @@procid
    exec sp_getProcname @PROCID, @PROCNAME output
	
    exec http..sp_audit @PROCNAME

Example

Here's an example that does not call sp_getProcname. Can you spot the error?

    create procedure sp_example
    as
        /*
        ** Call sp_audit and pass my procedure name to it.
        */
        exec http..sp_audit "sp_exumple"
        return 0

sp_html_frame

This procedure allows you to build frames in your HTML application. Frames are fairly easy to construct, but this procedure will do it automatically so you don't have to worry about how it's actually done.

When PeopleSoft was installed last year, all of the web reports that were created used a common convention that made it possible to develop these applications in the shortest time possible while providing a common look and feel to every report.

The common look and feel was accomplished by a stored procedure called sp_html_frame. This look and feel was not only seen by the user in what was rendered on their browser, but also by the developer because the internal working of every procedure followed the same convention. The point is, without close inspection it is difficult to tell the difference between Personnel Roster #5 and Employees Compensation Report.

Here is the prototype of sp_html_frame:

    create procedure sp_html_frame
    (   @BUTTON            varchar(92) = NULL      -- NULL or name of frame to build.
    ,   @PROCNAME          varchar(92) = NULL      -- Name of procedure.
    ,   @VERSION           varchar(60) = NULL      -- Version of procedure.
    ,   @TITLE             varchar(92) = NULL      -- Title on North frame.
    ,   @NORTH             varchar(10) = "70"      -- Size of north frame.
    ,   @CENTER            varchar(10) = NULL      -- Size of center frame.
    ,   @WEST              varchar(10) = NULL      -- Size of west frame.
    ,   @EAST              varchar(10) = NULL      -- Size of east frame.
    ,   @SOUTH             varchar(10) = NULL      -- Size of south frame.
    ,   @onLoad            varchar(255)= NULL      -- JavaScript onLoad event.
    ,   @BORDER            varchar(10) = "1"       -- Size of border around each frame.
    )
BUTTON is the single most important parameter passed to sp_html_frame because it lies at the heart of how it and your application work together. You will pass values for BUTTON to sp_html_frame and sp_html_frame will pass values back to your application; for example, to create the frames, a value of NULL is passed.

The frames are called NORTH, SOUTH, EAST, WEST, and CENTER. That is what they are called and that is how you will refer to them in your application. The NORTH frame is required and is built automatically by sp_html_frame. The other frames are optional are are built only when non-null values are given to their name.

When all frames are defined they resemble the following layout:

North
West
Center
East
South

A "hidden" feature of sp_html_frame is that it adds a log entry to an audit file each time it is called with a null value for BUTTON. The point here is that the audit file allows us to monitor the number of times your application is executed and who is executing it.

Example 1

Although sp_html_frame builds the frames, it is up to your application to populate those frames (except the NORTH frame, of course). And like sp_html_frame the prototype for your application will declare BUTTON.

BUTTON's sole purpose is to represent the state of your application. When it is null, your application is initializing and will call sp_html_frame. When it is not null, its value might be the name of the frame you are to populate, or it might be the value of a button on one of your form screens.

    /******************************************************************************/
    /*                                                                            */
    /* PATCH 001 Initial release                                     (08/01/1999) */
    /*                                                                            */
    /******************************************************************************/
    create procedure sp_example_1
    (   
        @BUTTON varchar(30) = NULL
    )
    as
    declare
        @PROCID                         int                 -- @@procid
    ,   @PROCNAME                       varchar(92)         -- db_name().owner.name.
    ,   @TITLE                          varchar(64)         -- Report title.
    ,   @VERSION                        varchar(16)         -- Report version.

        /*
        ** Initialize...
        */
        select @PROCID = @@procid
        exec sp_getProcname @PROCID, @PROCNAME output
        select @VERSION = "Version 1.01"
        select @TITLE = DESCRIPTION
        from   http..HTTP_REGISTER
        where  PROCNAME = @PROCNAME

        if @BUTTON is NULL
        begin
            /*
            ** Tell sp_html_frame to build the frames.
            */
            exec sp_html_frame

                @BUTTON = NULL
            ,   @PROCNAME = @PROCNAME
            ,   @VERSION = @VERSION
            ,   @TITLE = @TITLE
            ,   @SOUTH = "*"
            return 0
        end

        if @BUTTON = "SOUTH"
        begin
            /*
            ** sp_html_frame is telling me to populate the south frame.
            */
            select '<HTML>'
            ,      '<BODY BGCOLOR=WHITE>'
            ,      '<CENTER>'
            ,      replicate('<BR>',20)
            ,      'South'
            ,      '</CENTER>'
            ,      '</BODY>'
            ,      '</HTML>'
            return 0
        end

Example 1 - explained

The call to sp_getProcname returns the name of your stored procedure, including the database it belongs to and its owner. You'll avoid yourself a lot of head-aches if you always refer to @PROCNAME instead of using a literal string.

    select @PROCID = @@procid
    exec sp_getProcname @PROCID, @PROCNAME output
And besides, if you misspell @PROCNAME, you'll get a syntax error. If you misspell something inside of a string, it will compile just fine.

PROCNAME is passed to sp_html_frame and is displayed in the upper-left hand corner of the NORTH frame. Your users probably don't care about this name, but you do.


VERSION is useful too, and like PROCNAME it is also displayed on the NORTH frame. It's suppose to be incremented after each change to your application.

    select @VERSION = "Version 1.01"
Six months from now you might have to debug this application. If the version displayed by the application is different from the source code you are looking at, you may be well on your way to discovering the problem.

The single line patch comments at the top of this procedure go hand-in-hand with the version number of your program. Everytime a change is made to the source (no matter how trivial), add a description of that change to the patch heading and increment the version number.


In the Naked Gun series staring Leslie Neilson, one of the funnier bits was at the beginning of the show when the announcer would tell you the name of the episode while the name was printed at the bottom of the screen. The two were never the same, you see.

    select @TITLE = DESCRIPTION
    from   http..HTTP_REGISTER
    where  PROCNAME = @PROCNAME
Instead of hard-wiring your application's title inside of the application, why not pick it up from the http register so that the two are always the same?


In this application we are using only two frames: NORTH and SOUTH. The remaining frames can be passed as NULL or omitted from the call to sp_html_frame.

    exec sp_html_frame

        @BUTTON = NULL
    ,   @PROCNAME = @PROCNAME
    ,   @VERSION = @VERSION
    ,   @TITLE = @TITLE
    ,   @SOUTH = "*"
The frame names are in upper-case, so if you are running this on a case-sensitive server, you will want to make sure case them correctly. The expression
    @SOUTH = "*"
is telling sp_html_frame that you want a SOUTH frame and that it should occupy the remaining space on your browser's window, after the NORTH frame has been defined.

sp_html_frame will cause your application to be invoked for each frame that is not NULL, and it does this by passing that frame name for the value of BUTTON. In this example, BUTTON will be assigned to SOUTH. Your application will test for this value and populate the frame that corresponds to this value.

    if @BUTTON = "SOUTH" 
    begin
        /*
        ** sp_html_frame is telling me to populate the south frame.
        */
    end
Obviously, this is where things start to get interesting. This is where you might create your form screen or generate your report if no user input is required.

Example 1 - illustrated

If you compile and execute the above procedure, the results should look like this:

sp_example_1 Version 1.0
Frame Example 1
South

Example 2

The next example will explore more deeply how BUTTON is used to control state information. It will create a simple form screen for looking up employees. When the employee number changes, an onChange event will cause the application to look up this number and fill in the remaining fields. It does this without redrawing the form screen:

    /******************************************************************************/
    /*                                                                            */
    /* PATCH 001 Initial release                                     (08/01/1999) */
    /* PATCH 002 Add form screen and JavaScript to look up employees (08/02/1999) */ 
    /*                                                                            */
    /******************************************************************************/
    create procedure sp_example_2
    (
        @BUTTON             varchar(30) = NULL     -- State of procedure.
    ,   @employeenumber     varchar(5)  = NULL     -- employee number.
    )
    as
    declare
        @PROCID             int                    -- @@procid
    ,   @PROCNAME           varchar(92)            -- db_name().owner.name.
    ,   @TITLE              varchar(64)            -- Report title.
    ,   @VERSION            varchar(11)            -- Report version.
    ,   @onLoad             varchar(255)           -- JavaScript onLoad event.

        select @PROCID = @@procid
        exec sp_getProcname @PROCID, @PROCNAME output
        select @VERSION = "Version 1.02"
        select @TITLE = DESCRIPTION
        from   http..HTTP_REGISTER
        where  PROCNAME = @PROCNAME
    
        if @BUTTON is NULL
        begin
            exec sp_html_frame
        
                @PROCNAME    = @PROCNAME
            ,   @VERSION     = @VERSION
            ,   @TITLE       = @TITLE
            ,   @SOUTH       = "*"
        
            return 0
        end
    
        if @BUTTON = "SOUTH"
        begin
            /*
            ** Build the Center frame.
            */
            select '<HTML>'
            ,      '<BODY BGCOLOR=WHITE onLoad="window.document.FORM.employeenumber.focus();window.document.FORM.employeenumber.select();">'
            ,      '<CENTER>'
            ,      replicate('<BR>',2)
            ,      '<FONT SIZE=5 COLOR=NAVY><B><I>' + @TITLE + '</I></B></FONT>'
            ,      replicate('<BR>',2)

            exec sp_html_form
        
                @NAME = "FORM" 
            ,   @PROCNAME = @PROCNAME
            ,   @FORMAT = "SUPPRESSED"
            ,   @METHOD = "POST"
            ,   @TARGET = "NORTH" 

            exec sp_html_input
        
                @TYPE = "HIDDEN"
            ,   @NAME = "BUTTON"
            ,   @VALUE = "LOOKUP"  

            select '<TABLE BORDER=4 BGCOLOR=LIGHTGREY CELLPADDING=10 CELLSPACING=1>'
            ,      '<TR>'
            ,      '<TD>'
            ,      '<TABLE BORDER=0 WIDTH=100%>'
            ,      '<TR>'
            ,      '<TD>EMPLID:</TD>'
            ,      '<TD>'
        
            exec sp_html_input
        
                @TYPE = "TEXT"
            ,   @NAME = "employeenumber"
            ,   @SIZE = "5"
            ,   @MAXLENGTH = "5"
            ,   @onChange = "this.form.submit();"
        
            select '</TD></TR><TR><TD>NAME:</TD><TD>'
        
            exec sp_html_input
        
                @TYPE = "TEXT"
            ,   @NAME = "cn"
            ,   @SIZE = "30"

            select '</TD></TR><TR><TD>TITLE:</TD><TD>'
        
            exec sp_html_input
        
                @TYPE = "TEXT"
            ,   @NAME = "title"
            ,   @SIZE = "30"
        
            select '</TD></TR><TR><TD>E-MAIL ADDRESS:</TD><TD>'
        
            exec sp_html_input
        
                @TYPE = "TEXT"
            ,   @NAME = "mail"
            ,   @SIZE = "30"        

            select '</TD></TR></TABLE>'
            ,      '</TD></TR></TABLE>'
            ,      '<P>'
            ,      replicate('<BR>',2)
            ,      '<FONT COLOR=NAVY>Enter an employee number and press the TAB key</FONT>'
            ,      '</CENTER>'
        
            exec sp_html_form
        
            select '</BODY>'
            ,      '</HTML>'
            return 0
        end
    
        if @BUTTON = "LOOKUP"
        begin
            exec sp_html_frame
        
                @PROCNAME     = @PROCNAME
            ,   @VERSION      = @VERSION
            ,   @TITLE        = @TITLE
            ,   @WEST         = NULL
            ,   @BUTTON       = "NORTH"
        
            select '<SCRIPT>'
            ,      'parent.SOUTH.document.FORM.cn.value="'
            ,      cn
            ,      '";'
            ,      'parent.SOUTH.document.FORM.title.value="'
            ,      title
            ,      '";'
            ,      'parent.SOUTH.document.FORM.mail.value="'
            ,      mail
            ,      '";'
            ,      'parent.SOUTH.document.FORM.employeenumber.focus();'
            ,      'parent.SOUTH.document.FORM.employeenumber.select();'
            ,      '</SCRIPT>'
            from   http..ldif_view
            where  employeenumber = @employeenumber
        
            if ( @@rowcount = 0 )

            select '<SCRIPT>'
            ,      'parent.SOUTH.document.FORM.cn.value="";'
            ,      'parent.SOUTH.document.FORM.title.value="";'
            ,      'parent.SOUTH.document.FORM.mail.value="";'
            ,      'parent.SOUTH.document.FORM.employeenumber.focus();'
            ,      'parent.SOUTH.document.FORM.employeenumber.select();'
            ,      '</SCRIPT>'

            return 0        
        end

Example 2 - explained

Our form screen is built once. From then on out we'll use JavaScript to populate the fields on this form. We can do this because the TARGET for our form is set to NORTH.

    exec sp_html_form
        
        @NAME = "FORM" 
    ,   @PROCNAME = @PROCNAME
    ,   @FORMAT = "SUPPRESSED"
    ,   @METHOD = "POST"
    ,   @TARGET = "NORTH"
The other important bit is that we set the value of BUTTON to "LOOKUP" when the form is submitted:

    exec sp_html_input
        
        @TYPE = "HIDDEN"
    ,   @NAME = "BUTTON"
    ,   @VALUE = "LOOKUP"
Although the entire form is submitted, only the value for employee number is required. Many times you don't want to submit the entire form because that might be reserved for something else, like updating this record. To get around this our onChange code could have instead copied the employee number to another form and submit that instead.

Again, the result of our submit is targeted at the NORTH frame and BUTTON is assigned to LOOKUP. The code for that function first calls sp_html_frame to redraw the NORTH frame. The result is that the NORTH flickers ever so briefly.

The JavaScript that is created then populates the fields on our form. Since our code is now running in the NORTH frame, it qualifies these field names appropriately:

    <SCRIPT>
    parent.SOUTH.document.FORM.cn.value="Mary Smith";
    </SCRIPT>
If the employee number doesn't exist, all the other fields are erased. We could have displayed an alert instead. The cleaniest way to do this, however, is by passing the onLoad code to sp_html_frame:

    exec sp_html_frame
        
        @PROCNAME     = @PROCNAME
    ,   @VERSION      = @VERSION
    ,   @TITLE        = @TITLE
    ,   @WEST         = NULL
    ,   @BUTTON       = "NORTH"
    ,   @onLoad       = "alert('Oops!\n \nEmployee number not found.');"
Many times when you are calling sp_html_frame to rebuild the NORTH frame, you will want to use this feature. It may be nothing more than an alert to confirm that that an update was successful or unsuccessful, but I might also use it to ask the user a question before comitting their request:

    exec sp_html_frame
        
        @PROCNAME     = @PROCNAME
    ,   @VERSION      = @VERSION
    ,   @TITLE        = @TITLE
    ,   @WEST         = NULL
    ,   @BUTTON       = "NORTH"
    ,   @onLoad       = "if (confirm('Are you sure?')) window.document.FORM.submit();"
where window.document.FORM contains everything needed to perform this action.

Because I modified the original source, I commented this change in the patch heading and incremented the version number.

Example 2 - illustrated

sp_example_2 Version 1.0
Frame Example 2
EMPLID
NAME
TITLE
E-MAIL ADDRESS

Enter an employee number and press the TAB key


Sybernet is a trademark of SRI International.
Copyright © 1996-1999 SRI International. All Rights Reserved.
Denis D. Workman / http://Sybernet.sri.com/