Listing 1

<!---
    flow_map.cfm

    Outer switch looks at current state
    Inner switch looks at current event
 --->

<!--- Include common variables or functions needed by model
and view code in this flow --->
<!--- libQuery.cfm contains query UDFs from cflib.org --->
<cfinclude template="util/libQuery.cfm">

<!--- BEGIN state transition map --->
<cfswitch expression="#Variables.currentState#">

    <!--- In init state --->
    <cfcase value="init">
    	<cfinclude template="model/initflow.cfm">
    	<cfswitch expression="#Variables.thisEvent#">
    	<cfcase value="startFlow">
    		<cfset Variables.nextState = "choose_customer">
    	</cfcase>
    	</cfswitch>
    </cfcase>

    <!--- In choose_customer state --->
    <cfcase value="choose_customer">
    	<cfswitch expression="#Variables.thisEvent#">
    	<cfcase value="next">
            <cfinclude template="model/act_saveCustomerSelection.cfm">
    		<cfset Variables.nextState = "choose_products">
    	</cfcase>
    	</cfswitch>
    </cfcase>

    <!--- In choose_products state --->
    <cfcase value="choose_products">
        <cfinclude template="model/act_saveProductSelection.cfm">
    	<cfswitch expression="#Variables.thisEvent#">
    	<cfcase value="selectProduct">
    		<cfset Variables.nextState = "choose_products">
    	</cfcase>
        <cfcase value="finish">
            <cfset Variables.nextState = "review_order">
        </cfcase>
        <cfcase value="changeCustomer">
    		<cfset Variables.nextState = "choose_customer">
        </cfcase>
    	</cfswitch>
    </cfcase>

</cfswitch>
<!--- END state transition map --->


Listing 2

<!--- 
	stateful_flow_controller.cfm
	David Chandler, 12/2/03

	This file implements a simple state machine for application flow
	control.On first entry, it sets up a data structure in the session
	space that will be used to hold data from the successive forms as
	the user moves through the flow.

	The controller keeps track of the flow state, beginning in the init state.
	Each button or hyperlink in the flow is assigned an event name.
	The controller looks for the event in the URL and moves to the next
	state based on the combination of previous state and event.
 --->

<!--- Set default event --->
<cfif StructKeyExists (URL, "event")>
    <cfset Variables.thisEvent = URL.event>
<cfelseif StructKeyExists (Form, "event")>
    <cfset Variables.thisEvent = Form.event>
<cfelse>
    <cfset Variables.thisEvent = "">
</cfif>

<cfif NOT StructKeyExists (Session, "sFlowData") OR Variables.thisEvent is "startFlow">
	<!--- If structure not defined, initialize the flow --->
    <cfset Session.sFlowData = StructNew ()>
	<cfset Session.sFlowData.currentState = "init">
    <cfset Variables.thisEvent = "startFlow">
    <!--- Create space for user data that belongs to this flow --->
    <cfset Session.sUserData = StructNew ()>
</cfif>

<!--- Create reference to struct that holds user's data --->
<cfset thisFlow = Session.sUserData>

<!--- Get currentState variable for flow map --->
<cfset Variables.currentState = Session.sFlowData.currentState>

<cfif Len (Variables.thisEvent)>
    <!--- Include flow map --->
    <cfinclude template="flow_map.cfm">
    <!--- If nextState is valid --->
    <cfparam name="Variables.nextState" default="">
    <cfif Len (Variables.nextState)>
        <!--- Advance the state machine --->
		<cfset Session.sFlowData.currentState = Variables.nextState>
    <cfelse>
        <!--- currentState and event pair are not valid --->
        <cfoutput>Event #Variables.thisEvent# is invalid in state
		#Session.sFlowData.currentState#.</cfoutput>
        <cfabort>
    </cfif>
</cfif>

<!--- include view code for current state --->
<cfinclude template="views/#Session.sFlowData.currentState#.cfm">


Listing 3

<CFQUERY NAME="qCustomers" DATASOURCE="Northwind">
SELECT      CustomerID, CompanyName
FROM         dbo.Customers
</CFQUERY>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<html>
<head>
	<title>Choose Customer</title>
    <SCRIPT src="./typeahead.js"></SCRIPT>
</head>

<body>
<cfmodule template="../showMsg.cfm">

<form action="flowctl.cfm" method="post" id="mainForm">
<select name="customerID" size="1" id="customerID">
<option value="">Select a customer...
<cfoutput query="qCustomers">
    <option value="#qCustomers.CustomerID#">#qCustomers.CompanyName#
</cfoutput>
</select>
<input type="Submit" name="event" value="next">
</form>

<SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript">
<!--
mainForm.customerID.onkeydown = typeAhead;
//-->
</SCRIPT>

</body>
</html>

Listing 4

<!--- init array of selected products --->
<cfparam name="thisFlow.sOrder.aProductSelections" default="#ArrayNew (1)#">

<CFQUERY NAME="qProducts" DATASOURCE="Northwind">
SELECT      ProductID, ProductName
FROM         dbo.Products
</CFQUERY>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<html>
<head>
	<title>Select Products</title>
</head>

<body>
<cfmodule template="../showMsg.cfm">
<a href="flowctl.cfm">Start over</a>

<p>
<form action="flowctl.cfm" method="post" id="mainForm">
<cfoutput>
You have currently selected the following customer:<br>
<cfdump var="#thisFlow.sOrder.sCust#">
<input type="Submit" name="event" value="changeCustomer">
</cfoutput>
<p></p>

<select name="productID" size="1" id="productID">
<option value="">Select a product to add...
<cfoutput query="qProducts">
    <option value="#qProducts.ProductID#">#qProducts.ProductName#
</cfoutput>
</select>
<input type="Submit" name="event" value="selectProduct">
</p>

You have currently selected the following products:<br>
<cfdump var="#thisFlow.sOrder.aProductSelections#">
<P>
<input type="Submit" name="event" value="finish">
</form>

</body>
</html>