正如在 第 1 部分 中指出的,MySQL 是一种开放源码数据库。在我们的场景中,使用社区服务器版本,这是一个紧凑的数据库服务器,具有许多有用的特性。因为这个银行场景的实现基于开放源码产 品,所以 MySQL 和 Zend Core PHP 是合适的组合。Zend Core 本身支持 MySQL,还有各种支持 MySQL 管理和编程的工具。在我们的场景中,只使用 MySQL 命令行客户机执行 MySQL 的管理。我们将用 MySQL 数据库为这个场景建立银行帐户数据库。
-- This file is part of the End-to-End Ajax development article in -- the IBM developerWorks. This file contains a simple DB script to -- create a database and populate it with the data. -- -- Last Modified: May/10/2007 -- -- To execute the following statements in MySQL, do the following steps. -- 1) Start MySQL command line client. -- 2) Enter your MySQL admin password. -- 3) Type the following line by substituting <YOUR_SQL_FILE_DIR> with the -- directory name where the file is stored. -- source <YOUR_SQL_FILE_DIR>/bankdb.sql
-- -- Table structure for table 'BankDB' --
DROP DATABASE BankDB;
CREATE DATABASE BankDB;
USE BankDB;
CREATE TABLE account ( AccountHolderName VARCHAR(20) NOT NULL, AccountNumber INTEGER NOT NULL, CheckingBalance DOUBLE NOT NULL, StockName VARCHAR(6), StockQuantity INTEGER, StockValue DOUBLE, PRIMARY KEY(AccountHolderName, AccountNumber) );
-- -- Populating data for table 'account' --
insert into ACCOUNT values ('Frodo', 435245, 2344.45, 'GOOG', 100, 3453.32); insert into ACCOUNT values ('Sam', 928462, 7583.32, 'CSCO', 200, 5323.43); insert into ACCOUNT values ('Pippin', 234233, 3444.62, 'INTC', 300, 4213.76); insert into ACCOUNT values ('Merry', 642445, 1005.32, 'MSFT', 250, 1353.32); insert into ACCOUNT values ('Aragorn', 972321, 6424.24, 'HPQ', 525, 12043.94); insert into ACCOUNT values ('Gandalf', 432134, 5392.23, 'IBM', 400, 10043.78); insert into ACCOUNT values ('Legolas', 590134, 4313.82, 'DELL', 325, 5926.62);
PHP 最受人喜爱的特性之一是,它为访问不同厂商的数据库中的数据提供了简单且出色的支持,包括 MySQL。它提供了一种集成业务逻辑和数据库的简便有效的方法。在过去几年中,PHP 社区完成了几项改进,比如 PHP Data Objects(PDO),PDO 提供一个抽象层,无论使用哪种数据库服务器,这个抽象层都公开同样的 API 函数。有两种风格的 PHP 数据库 API 函数:过程式的和面向对象的。在这个场景中,我们使用针对 MySQL 的直接数据库 API 来获得一个基本的了解。PHP 提供两种访问 MySQL 的方法:
<?php /* ============================================================ Project: End-to-End-Ajax application development
Purpose: This is an example scenario to be used in an IBM developerWorks article.
Last modified: May/11/2007.
This PHP module provides the core bank teller logic required to access the customer related data from the database. These functions can be called by other PHP modules present in the Bank scenario. All the logic involved in bank teller actions are divided into these three core functions.
a) Get all account information b) Process Transaction (deposit or debit) c) Compute stock portfolio value
It uses direct mysqli functions to access the database as opposed to the PHP PDO functions. ============================================================ */ // These globals will be used to hold the following values: // 1) $link is for storing the database connection object. // 2) $dbResult is for storing the db query result object. // 3) $finalResult is an associative array where the results // from individual bank teller actions are packaged and // returned to the caller. global $link, $dbResult, $finalResult;
This function connects to the BankDB MySQL database. If the connection attempt is successful, it stores the connection object in a global scoped variable and returns true. If the connection attempt is not successful, then it returns false. ============================================================ */ function connect_to_db() { // We will use these global scoped variables here. global $link, $dbResult, $finalResult;
// Do a mysqli connect to the local BankDB database using // proper credentials. $link = mysqli_connect("localhost", "root", "webtech", "bankdb");
// Check if DB connection worked. if (mysqli_connect_errno()) { // It looks like there is some problem. // Get the MySQL error number and the error string. $resultMsg = "DB connection error: " . mysqli_connect_errno() . " [" . mysqli_connect_error() . "]"; // Store the results in an associative array. // Set the result of the operation as not a successful one. $finalResult["ResultCode"] = 1; // Set the result message. $finalResult["ResultMsg"] = $resultMsg; // Return false to indicate the DB connection failure. return (false); } else { // DB connection is good. return(true); } // End of if (mysqli_connect_errno()) } // End of function connect_to_db
This function closes the connection made earlier to the BankDB database. ============================================================ */ function close_connection_to_db() { // We will use these globally scoped variables. global $link, $dbResult;
// Close the connection if we have an active // DB connection object. if ($link != null) { if (($dbResult != null) && (is_object($dbResult))) { // If the DB result contains query data object, free it now. mysqli_free_result($dbResult); $dbResult = null; } // End of if ($dbResult != null)
// Time to close the DB connection. mysqli_close($link); // Set the connection object variable to null. $link = null; } // End of if ($link != null) } // End of function close_connection_to_db
This function reads all the account information stored in the BankDB and returns them in an associative array. ============================================================ */ function getAllAccountInformation() { // We will use these globally scoped variables. global $link, $finalResult, $dbResult;
// Get a connection to the BankDB. $result = connect_to_db();
// If db connection failed, return now. if ($result == false) { return ($finalResult); } // End of if ($result == false)
// Make an SQL statement to query all rows in the account table. $queryStr = "Select * from account"; // Make the SQL query. $dbResult = mysqli_query($link, $queryStr);
// Process if there are any query errors. if (mysqli_errno($link)) { // Close the connection first. close_connection_to_db(); // Set the error message in the final result assoc. array. $resultMsg = "DB read error: " . mysqli_errno($link) . " [" . mysqli_error($link) . "]"; // Set the application specific return code as FAILURE. $finalResult["ResultCode"] = 1; $finalResult["ResultMsg"] = $resultMsg; // Return the final result. return ($finalResult); } // End of if (mysqli_errno($link))
// Get the number of rows queried. $rowCount = mysqli_num_rows($dbResult);
// If the database is empty, return now. if ($rowCount <= 0) { // Close the connection first. close_connection_to_db(); // Set the return code and return message. $finalResult["ResultCode"] = 1; $finalResult["ResultMsg"] = "No accounts were found in BankDB."; // Return the final result. return ($finalResult); } // End of if ($rowCount <= 0)
// Set the counter to 0. $cnt = 0; // Create an array to hold all the fields from each row. $accountInfo = array();
// From the query result set, fetch data fields from each row. while($row = mysqli_fetch_assoc($dbResult)) { // Initialize it to null. $data = null; // Create an associative array on the fly and store the // individual data fields read from the current database row. $data["AccountHolderName"] = $row["AccountHolderName"]; $data["AccountNumber"] = $row["AccountNumber"]; $data["CheckingBalance"] = doubleval($row["CheckingBalance"]); $data["StockName"] = $row["StockName"]; $data["StockQuantity"] = intval($row["StockQuantity"]); $data["StockValue"] = doubleval($row["StockValue"]); // Now, store the entire associative array in to a regular array. $accountInfo[$cnt++] = $data; } // End of while($row = mysqli_fetch_assoc($dbResult))
// We finished reading all the account information from the database. // Set the final result code as success. $finalResult["ResultCode"] = 0; // Set the final result message as success. $finalResult["ResultMsg"] = "ReadAllAccountsInfoFromDB successful"; // Make the entire array holding all the account information into the // final result associative array. // $finalResult is an associative array containing an // regular array which in turn contains several associative arrays as // its elements. $finalResult["AccountInfo"] = $accountInfo; // Close the DB connection. close_connection_to_db(); // Return the final result array now. return($finalResult); } // End of function getAllAccountInformation
This function performs the bank teller account transaction. This function takes the account holder name, an amount and the transaction type as function parameter. The transaction type is 1 for deposit into checking account. The transaction type is 2 for debit from a checking account. After the transaction is done, it returns the snapshot of the account before this transaction and another snapshot of the account after this transaction. ============================================================ */ function accountTransaction($accountHolderName, $amount, $transactionType) { // We will use these globally scoped variables. global $link, $finalResult, $dbResult; // Get a database connection. $result = connect_to_db();
// If the connection failed, return now. if ($result == false) { return ($finalResult); } // End of if ($result == false)
// Make an SQL statement to read a particular account data for a // given account holder name. $queryStr = "Select * from account where AccountHolderName='$accountHolderName'"; // Perform the SQL query. $dbResult = mysqli_query($link, $queryStr);
// Did you encounter a DB error? if (mysqli_errno($link)) { // Close the connection now. close_connection_to_db(); // Set the DB error message. $resultMsg = "DB read error: " . mysqli_errno($link) . " [" . mysqli_error($link) . "]"; // Set the application-specific result code. $finalResult["ResultCode"] = 1; // Set the result message. $finalResult["ResultMsg"] = $resultMsg; // Return the associative array with the error result. return ($finalResult); } // End of if (mysqli_errno($link))
// How many rows were read from the DB? $rowCount = mysqli_num_rows($dbResult);
// If no rows are obtained from DB, return now. if ($rowCount <= 0) { // Close the connection now. close_connection_to_db(); // Setup the return value as not a successful one. $finalResult["ResultCode"] = 1; $finalResult["ResultMsg"] = "No accounts were found in BankDB."; // Return the final result. return ($finalResult); } // End of if ($rowCount <= 0)
// Store a record with the current checking balance. // Create an array to hold the pre-transaction and // post-transaction account snapshots. $accountInfo = array(); // Fetch the query result as an associative array. $row = mysqli_fetch_assoc($dbResult); $data = null; // Record the filed values from the database row into // a new associative array. $data["AccountHolderName"] = $row["AccountHolderName"]; $data["AccountNumber"] = $row["AccountNumber"]; $data["PreviousCheckingBalance"] = doubleVal($row["CheckingBalance"]); $data["StockName"] = $row["StockName"]; $data["StockQuantity"] = intval($row["StockQuantity"]); $data["StockValue"] = doubleVal($row["StockValue"]); // Store the pre-transaction account snapshot into a regular array. $accountInfo[0] = $data;
// Initialize the variables. $newBalance = 0.0; // Since we are doing a transaction that changes the // checking balance, we need to update the DB later. $updateDB = true;
if ($transactionType == 1) { // It is deposit. // Ensure that user wants to deposit an amount greater than 0. if ($amount > 0) { $newBalance = doubleval($data["PreviousCheckingBalance"]) + $amount; } else { // Deposit amount is 0. There is no need to do a database update. $updateDB = false; $newBalance = doubleval($data["PreviousCheckingBalance"]); } // End of if ($amount > 0) } else if ($transactionType == 2) { // It is debit. // Ensure that the user wants to debit an amount greater than 0 and // the amount is less than the money available in the checking account. if (($amount > 0) && ($amount < doubleval($data["PreviousCheckingBalance"]))) { $newBalance = doubleval($data["PreviousCheckingBalance"]) - $amount; } else { // Debit amount is either 0 or it is bigger than a allowed value. $updateDB = false; $newBalance = doubleval($data["PreviousCheckingBalance"]); } } // End of if ($transactionType == 1)
// Bank teller transaction is completed now. // Life is good. Send the result back. $finalResult["ResultCode"] = 0; $finalResult["ResultMsg"] = "AccountTransaction successful.";
// If we modified the current checking balance because of a // deposited or debited amount, then update the database. if ($updateDB == true) { // Prepare an SQL Update statement. $updateStr = "update account set CheckingBalance=$newBalance " . "where AccountHolderName='$accountHolderName'";
// Execute the update query. $dbResult = mysqli_query($link, $updateStr);
// See if there are any DB errors? if (mysqli_errno($link)) { // Close the connection to the database. close_connection_to_db(); // Prepare the error message to be returned. $resultMsg = "DB write error: " . mysqli_errno($link) . " [" . mysqli_error($link) . "]"; $finalResult["ResultCode"] = 1; $finalResult["ResultMsg"] = $resultMsg; // Return the error message. return ($finalResult); }
if ($dbResult == true) { // All fine. // Prepare to send back the result of this operation. $finalResult["ResultCode"] = 0; $finalResult["ResultMsg"] = "New balance for $accountHolderName has been stored in DB."; } else { $finalResult["ResultCode"] = 1; $finalResult["ResultMsg"] = "New balance for $accountHolderName could not be stored in DB."; // Return the final result that has an error. return($finalResult); } // End of if ($dbResult == true) } // End of if ($updateDB == true)
// We already stored the pre-transaction account snapshot in an // array at the top of this function. // Store a second record that will have the updated checking balance. $data = null; $data["AccountHolderName"] = $row["AccountHolderName"]; $data["TransactionAmount"] = $amount; $data["AccountNumber"] = $row["AccountNumber"]; $data["NewCheckingBalance"] = $newBalance; $data["StockName"] = $row["StockName"]; $data["StockQuantity"] = intval($row["StockQuantity"]); $data["StockValue"] = doubleval($row["StockValue"]); $accountInfo[1] = $data; $finalResult["AccountInfo"] = $accountInfo; // Close the DB connection. close_connection_to_db(); // Return the final result. return($finalResult); } // End of function accountTransaction.
This function stores the current portfolio value of a given account holder to the database. It takes the account holder name and the current stock price as input arguments. It reads the currently held position (in the Bank scenario, every account holder owns only a single company's stock. It is done to make the task simpler.) and computes the current market value. Then it updates the database with the new total stock value. ============================================================ */ function portfolioValue($accountHolderName, $stockPrice) { // We will use the globally scoped variables. global $link, $finalResult, $dbResult;
// Connect to the MySQL BankDB database. $result = connect_to_db();
// If error in connecting to the DB, return now. if ($result == false) { return ($finalResult); } // End of if ($result == false)
// Prepare the query string to get account info for a given account holder. $queryStr = "Select * from account where AccountHolderName='$accountHolderName'"; // Execute the MySQL query. $dbResult = mysqli_query($link, $queryStr);
// Are there any errors? if (mysqli_errno($link)) { // Error there. Close the db connection. close_connection_to_db(); // Prepare error message to be returned. $resultMsg = "DB read error: " . mysqli_errno($link) . " [" . mysqli_error($link) . "]"; $finalResult["ResultCode"] = 1; $finalResult["ResultMsg"] = $resultMsg; // Return the error. return ($finalResult); } // End of if (mysqli_errno($link))
// Did we read any rows from the DB? $rowCount = mysqli_num_rows($dbResult);
// If there are no matching rows in DB, return now. if ($rowCount <= 0) { // Close the DB connection. close_connection_to_db(); $finalResult["ResultCode"] = 1; $finalResult["ResultMsg"] = "No accounts were found in BankDB."; // Return the final result. return ($finalResult); } // End of if ($rowCount <= 0)
// Store a record with the current portfolio value. // Allocate an array to store the pre-transaction and the // post-transaction account snapshots. $accountInfo = array(); // Do a database fetch to get the results as an associative array. $row = mysqli_fetch_assoc($dbResult); // Store the data fields of a row to an associative array. $data = null; $data["AccountHolderName"] = $row["AccountHolderName"]; $data["AccountNumber"] = $row["AccountNumber"]; $data["CheckingBalance"] = doubleval($row["CheckingBalance"]); $data["StockName"] = $row["StockName"]; $data["StockQuantity"] = intval($row["StockQuantity"]); $data["PreviousPortfolioValue"] = doubleval($row["StockValue"]); // Store the associative array in a regular array. $accountInfo[0] = $data;
// Since the stock portfolio value has changed, update it in DB. $updateStr = "update account set StockValue=$newPortfolioValue " . "where AccountHolderName='$accountHolderName'";
// Perform an SQL update. $dbResult = mysqli_query($link, $updateStr);
// Check if there are any DB errors. if (mysqli_errno($link)) { // Close the connection to DB. close_connection_to_db(); // Prepare the error message to returned. $resultMsg = "DB write error: " . mysqli_errno($link) . " [" . mysqli_error($link) . "]"; $finalResult["ResultCode"] = 1; $finalResult["ResultMsg"] = $resultMsg; // Return the error message. return ($finalResult); } // End of if (mysqli_errno($link))
if ($dbResult == true) { // All fine. // Prepare the success message to be returned. $finalResult["ResultCode"] = 0; $finalResult["ResultMsg"] = "New stock value for $accountHolderName has been stored in DB."; } else { $finalResult["ResultCode"] = 1; $finalResult["ResultMsg"] = "New stock value for $accountHolderName could not be stored in DB."; // Update failed. Hence return the error result. return($finalResult); } // End of else in if ($dbResult == true)
// Store a second record that will have the updated portfolio value. // We have already stored the pre-transaction account snapshot in an array. // Let us store the post-transaction account snapshot also there. $data = null; $data["AccountHolderName"] = $row["AccountHolderName"]; $data["AccountNumber"] = $row["AccountNumber"]; $data["CheckingBalance"] = doubleval($row["CheckingBalance"]); $data["StockName"] = $row["StockName"]; $data["CurrentStockPrice"] = doubleval($stockPrice); $data["StockQuantity"] = intval($row["StockQuantity"]); $data["NewPortfolioValue"] = $newPortfolioValue; // Store the associative array in a regular array. $accountInfo[1] = $data; $finalResult["AccountInfo"] = $accountInfo; // Close the DB connection and return the result. close_connection_to_db(); return($finalResult); } // End of function portfolioValue. ?>
<!-- ============================================================ Project: End-to-End-Ajax application development
Purpose: This is an example scenario to be used in an IBM developerWorks article.
Last modified: May/12/2007.
This HTML file provides the required user-interface elements for the bank teller browser application. This is a single-page based Ajax browser application. It simply means that this application doesn't do any page fetches at all from the server other than the initial download of this single HTML file.
This file liberally makes use of <DIV>, <SPAN> and <TABLE> tags to do some of the well-known Ajax tricks. It is a self-contained file that contains all the HTML markup required by the bank teller application. ============================================================ --> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <!-- HTML document header describes the various properties of the document including its title and relationship with other documents such as CSS and JavaScript files. In the Bank Teller single-page application, the following documents will be used:
1) json.js (It is an open-source JSON parser developed by Douglas Crockford. 2) xhr.js (It is a generic utility that provides XHR related functions. 3) BankTeller.js (All the client-side logic in this JavaScript file) 4) BankTeller.css (All the styling needed for the browser application is here) --> <head> <title>Ajax Bank Teller Application</title> <script language"JavaScript" type="text/javascript" src="json.js"></script> <script language"JavaScript" type="text/javascript" src="xhr.js"></script> <script language"JavaScript" type="text/javascript" src="BankTeller.js"></script> <link rel="stylesheet" style="text/css" href="BankTeller.css" _fcksavedurl="BankTeller.css" media="all" /> </head>
<!-- Set an event handler to initialize the application-specific stuff. This event handler will be triggered during page load time. In this event handler, initial screen for the bank teller application will be set and the other screens will be hidden. --> <body οnlοad="initOnPageLoad();"> <!-- This application has seven different sections that are divided using the <DIV> elements. Those sections are: 1) Main section 2) Teller Options (Menu) section 3) Deposit section 4) Debit section 5) Stock portfolio section 6) Bank teller action result section 7) Page footer section
All these sections will be shown selectively depending the application context where the user is in at a given time. This is done in the client side logic coded in JavaScript. This technique provides the dynamism and rich response to user actions. It elevates the user experience level drastically as compared to the traditional Web applications developed using the click and wait page refresh model. --> <!-- The following the main DIV container for the entire application. All the other sections are children of this. They all hang on to this DIV container node in DOM. --> <div id="mainPage" title="This is an Ajax based single-page browser application. No browser Back button please. HTML download from server occurs ONLY ONCE. From then on, back-end server is contacted asynchronously ONLY for data exchanges."> <h1 title="This web application provides Bank teller services."> Bank Teller Operations </h1>
<!-- The tellerOptions DIV element holds the table that will provide the user with available teller options to select from. This container holds the menu choices for the entire bank teller application. --> <div id="tellerOptions" title="You can perform these teller operations."> <center><font color="Olive"> <u>Click an operation below</u> </font></center>
<!-- Each menu option is a HTML text that is embedded within a SPAN tag. That will enable us to set the mouseover and mouseout events for that text element. We can combine those events with CSS styling to emulate a desktop-like menu behavior. When the user clicks on that menu, then the corresponding application logic can be performed.
These different menu option are laid out inside a TABLE tag as individual rows. --> <table class="tellerOptionsTable" align="center"> <tr> <!-- Note that we are using class attribute in span to style the td value so that we can dynamically assign style classes (in JavaScript) as the user moves the mouse over different teller operation names. If we use the id attribute to CSS style, we can't dynamically assign styles in JavaScript. --> <!-- The event handler functions are called with a function parameter that indicates the menu option number. i.e. 1, 2 or 3. --> <!-- Deposit menu option --> <td> <span id="tellerOptionsLink1" class="tellerOptionsLink" title="Deposit to an account." οnmοuseοver="changeOptionsLinkStyleForSelection(1);" οnmοuseοut="changeOptionsLinkStyleToDefault(1);" οnclick="processTellerOperation(1);"> 1) Deposit to an account </span> </td> </tr>
<tr> <!-- Debit menu option --> <td> <span id="tellerOptionsLink2" class="tellerOptionsLink" title="Debit from an account." οnmοuseοver="changeOptionsLinkStyleForSelection(2);" οnmοuseοut="changeOptionsLinkStyleToDefault(2);" οnclick="processTellerOperation(2);"> 2) Debit from an account </span> </td> </tr>
<tr> <!-- Get stock portfolio value menu option --> <td> <span id="tellerOptionsLink3" class="tellerOptionsLink" title="Get Stock portfolio value." οnmοuseοver="changeOptionsLinkStyleForSelection(3);" οnmοuseοut="changeOptionsLinkStyleToDefault(3);" οnclick="processTellerOperation(3);"> 3) Current portfolio value </span> </td> </tr> </table> </div> <!-- End of <div id="tellerOptions"> -->
<!-- The tellerOptions DIV element holds a HTML form required for depositing money to an account. --> <div id="depositAction" title="Deposit to an account" class="depositAction"> <!-- This HTML form provides the UI elements for deposit action --> <form name="depositActionForm"> <span id="depositActionFormTitle" class="depositActionFormTitle"> Deposit to an account </span>
<!-- This table provides a drop-down selection box and an input control to enter the deposit amount. --> <table class="depositActionTable" align="center"> <tr> <td>Select Account Owner:</td> <td> <select id="depositAccountOwner" size=7 name="depositAccountOwner"> </select> </td> </tr>
<!-- This table provides an action button. When pressed, it will trigger an onclick event handler that will perform the required client-side logic. --> <table align="center" class="depositAmountButtonTable"> <tr> <td> <input id="depositAmountActionButton" value="Deposit" type="button" title="Click here to deposit to an account." οnclick="depositAmountAction_Async();"/> </td> </tr> </table> </form> </div> <!-- End of <div id="depositAction"> -->
<!-- The tellerOptions DIV element holds a HTML form required for debiting money from an account. --> <div id="debitAction" title="Debit from an account" class="debitAction"> <form name="debitActionForm"> <span id="debitActionFormTitle" class="debitActionFormTitle"> Debit from an account </span>
<!-- This table provides an action button. When pressed, it will trigger an onclick event handler that will perform the required client-side logic. --> <table align="center" class="debitAmountButtonTable"> <tr> <td> <input id="debitAmountActionButton" value="Debit" type="button" title="Click here to debit from an account." οnclick="debitAmountAction_Async();"/> </td> </tr> </table> </form> </div> <!-- End of <div id="depositAction"> -->
<!-- The portfolioOption DIV element holds the form for updating the current portfolio value. --> <div id="portfolioAction" title="Update portfolio value" class="portfolioAction"> <form name="portfolioActionForm"> <span id="portfolioActionFormTitle" class="portfolioActionFormTitle"> Get portfolio value </span>
<!-- This table provides an action button. When pressed, it will trigger an onclick event handler that will perform the required client-side logic. --> <table align="center" class="portfolioValueButtonTable"> <tr> <td> <input id="portfolioActionButton" value="Get Stock Portfolio Value" type="button" title="Click here to get the portfolio value." οnclick="portfolioAction_Async();"/> </td> </tr> </table> </form> </div> <!-- End of <div id="portfolioAction"> -->
<!-- The tellerActionResult DIV element holds the form for getting portfolio value. --> <div id="tellerActionResult" title="Result of teller operation" class="tellerActionResult"> <form name="tellerActionResultForm"> <span id="tellerActionResultTitle" class="tellerActionResultTitle"> Result from Teller Operation </span>
<!-- This table includes a text area that summarizes the result of the operation performed by the teller. --> <table class="tellerActionResultTable" align="center"> <tr> <td> <textarea id="tellerActionResultArea" rows="15" cols="80" readonly></textarea> </td> </tr> </table> </form> </div> <!-- End of <div id="tellerActionResult"> -->
<!-- The pageFooter DIV element holds the footer information such as the currently logged in user and Go to main menu option. This footer section will be stapled at the bottom of every other section of this application. --> <div id="pageFooter"> <h1></h1>
<table id="footerTable" class="footerTable" align="center"> <tr> <!-- Note that we are using class attribute in span to style the td value so that we can dynamically assign style classes (in JavaScript) as the user moves the mouse over the footer options. If we use the id attribute to CSS style, we can't dynamically assign styles in JavaScript. --> <td> <span id="currentUserDisplay" class="currentUserDisplay" title="Currently logged in user's name."> Teller: John Doe </span> </td>
<!-- The event handler functions are called with a function parameter that indicates which footer option to be highlighted. --> <td> <span id="goToMainMenuLink" class="goToMainMenuLink" title="Click here to go to main menu." οnmοuseοver="changeFooterLinkStyleForSelection(1);" οnmοuseοut="changeFooterLinkStyleToDefault(1);" οnclick="processFooterOperation(1);"> Back to main menu </span> </td> </tr> </table>
</div> <!-- End of <div id="PageFooter"> --> </div> <!-- End of <div id="mainPage"> --> </body> </html>
/* ============================================================ Project: End-to-End-Ajax application development
Purpose: This is an example scenario to be used in an IBM developerWorks article.
Last modified: May/12/2007.
This file provides CSS rules that are used to style the UI controls for the bank teller browser application. This application doesn't address all the features of CSS. However, basic and important aspects of CSS are included here in the context of the bank teller application. ============================================================ */ /* This is a HTML selector that redefine the body tag with font and background attributes. */ body {
/* This is a HTML selector used to style the <h1> element. */ h1 { color: #cc6600; border-bottom: medium double #888888; font-size: 1.7em; }
/* This CSS ID class is applicable for the DIV element that holds the table in which all the available teller operations are listed. */ #tellerOptions { margin-left: 3%; margin-right: 3%; padding-top: 10px; padding-bottom: 10px; border: red; border-width: thick; border-style: double; border-collapse: collapse; }
/* This qualified dependent selector applies style to the teller option table. */ table.tellerOptionsTable td { padding-top: 10px; padding-bottom: 10px; }
/* This CSS generic class is applicable to style the margins for the tables in the teller actions form. */ .tellerOptionsTable, .depositActionTable, .debitActionTable, .portfolioActionTable { margin-top: 30px; }
/* This CSS generic class is applicable for the SPAN elements that hold the clickable text representing various teller operations. User can select an operation to perform from this list. Generic class is used here so that inside of JavaScript, we can dynamically change the style of the text representing the teller operations as the user mouseover or mouseout of those text regions defined within the SPAN elements. The following is the default style when there is no user mouse activity or when the user moves the mouse out. */ .tellerOptionsLink { cursor: pointer; background-color: rgb(220, 220, 220); color: blue; font-weight: bold; font-style: normal; text-decoration: none; border-style: none; }
/* This qualified CSS generic class defines the cell padding for the buttons in the teller actions form and others. */ table.depositAmountButtonTable td, table.debitAmountButtonTable td, table.portfolioValueButtonTable td, table.tellerActionResultTable td { padding-left: 25px; padding-right: 25px; }
/* This qualified CSS generic class defines the margin for the tables holding the teller actions form and others. */ table.depositAmountButtonTable, table.debitAmountButtonTable, table.portfolioValueButtonTable, table.tellerActionResultTable { margin-top: 40px; }
/* This qualified CSS generic class is applicable in order to set the cellpadding for the table <td> where the footer options are listed. */ table.footerTable td { padding-left: 100px; padding-right: 100px; }
/* This qualified CSS generic class is applicable in order to set the margin for the table where the footer options are listed. */ table.footerTable { margin-top: 20px; }
/* This CSS generic class is applicable for the static display of the current username who is logged into the system. */ .currentUserDisplay { cursor: default; background-color: rgb(220, 220, 220); color: darkred; font-weight: normal; font-style: normal; }
/* This CSS generic class is applicable for the SPAN elements that hold the clickable text representing footer options. User can select to logout from the system. Generic class is used here so that inside of JavaScript, we can dynamically change the style of the text representing the logout link as the user mouseover or mouseout of those text regions defined within the SPAN elements. The following is the default style when there is no user mouse activity or when the user moves the mouse out. */ .gotoMainMenuLink { cursor: pointer; background-color: rgb(220, 220, 220); color: black; font-weight: bold; font-style: normal; text-decoration: none; border-style: none; }
/* This CSS ID class is applicable for the Teller actions button and others. */ #depositAmountActionButton, #debitAmountActionButton, #portfolioActionButton { background-color: saddlebrown; color: white; border: purple; border-width: thin; border-style: inset; font-weight: bold; font-style: normal; font-size: 90% }
/* The following overrides a style defined previously in the same class. */ #debitAmountActionButton { background-color: midnightblue; }
/* The following overrides a style defined previously in the same class. */ #portfolioActionButton { background-color: teal; }
/* ============================================================ Project: End-to-End-Ajax application development
Purpose: This is an example scenario to be used in an IBM developerWorks article.
Last modified: May/12/2007.
This JavaScript file provides specific functions related to XHR (XML HTTP Request). The logic explained here is a generic one which is being used hundreds of other Web applications. This logic can be found in any Ajax related book or article that describes the inner workings of XHR. ============================================================ */
This function creates an XHR object that is browser agnostic. As of this writing, XHR is somewhat browser dependent. We can divide XHR support in two major classes of browsers i.e. Internet Explorer and non-IE browsers. Then, within IE, there are different implementations of XHR in pre-IE6 and the rest. In summary, Microsoft supports XHR through its ActiveX. After listening to complaints from the Web developer community, Microsoft realized the need to expose XHR as a native browser object as done in other browsers. The rumor is that we will soon see XHR support in IE browsers via XMLHttpRequest object. Until then, we have to take care of browser dependency as shown in this function. ============================================================ */ function createRequest() { // Define a local variable and set it to null. var request = null;
// Try different things to see which browser is being used. try { // Non Microsoft browsers (Firefox, Safari etc.) request = new XMLHttpRequest(); } catch(trymicrosoft) { try { // IE6 and above. request = new ActiveXObject("Msxml2.XMLHTTP"); } catch(othermicrosoft) { try { // Older versions of IE i.e. pre-IE6. request = new ActiveXObject("Microsoft.XMLHTTP"); } catch(failed) { // No support for XHR request = null; } // End of catch(failed) } // End of catch(othermicrosoft) } // End of catch(trymicrosoft)
// Check if we have a valid XHR object. if (request == null) { alert("Error creating the XMLHttpRequest object!"); } else { // Return the valid XHR object that was created. return(request); } // End of if (request == null) } // End of function createRequest.
This function transmits any arbitrary data to a server URL. It takes four arguments: 1) XHR object 2) Callback function (if any) to receive the server response 3) URL to which the content is to be sent. 4) Data to be sent.
The depending on the input parameters, it will either communicate with the server asynchronously or synchronously to do the data interchange. It uses the POST verb of HTTP to do the REST-style call. ============================================================ */ function sendHttpRequest(request, callbackFunction, url, postData) { // Initialize a local variable to false. // This variable indicates if we need to communicate with the // server in an asynchronous mode. var async_request = false;
// Did the caller give us a Callback function? if (callbackFunction != null) { // We have a callback function. // Set that function to XHR object's onreadystatechange property. request.onreadystatechange = callbackFunction; // Set the local variable to indicate that // we need to send and receive the response in a non-blocking mode // i.e. Async. async_request = true; }
// Open a HTTP connection to the provided URL. request.open("POST", url, async_request); // Set a HTTP request header. request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); // Send it now. // If async mode was not true, then this call will block until a // HTTP response is received from the server. // Otherwise, this statement will just send and return without waiting for a // server response. var response = request.send(postData);
if (async_request == false) { // We sent the request synchronously. // Hence, return the response received from the server. return(response); } else { // If the request was made asynchronously, we need not bother to return // anything meaningful. The response will be sent directly to the // callback function that was provided by the caller. return(true); } } // End of function sendHttpRequest.
Senthil Nathan 是位于纽约 Hawthorne 的 IBM T.J. Watson Research Center 的一位高级软件工程师。在为不同类型的企业应用程序构建软件方面,他有 22 年经验。他当前感兴趣的领域包括 SOA、Web 服务、Java 2 Platform, Enterprise Edition(J2EE)、PHP、Ruby On Rails、Web 2.0 和 Ajax 开发。