How to Preserve State Information
Description
Web applications are inherently stateless. In practical application, however, you need to preserve state.
Discussion
When a user first visits a web site, a web application will typically create a new 'session'. Subsequent visits by the user to the web site are part of the same 'session'. Eventually, the session expires. When this happens, a new session is created the next time the user visits the website.
In Alpha Anywhere applications, you will often want to set variables that apply only to a user's session. This is done using session variables. Session variables allow you to save and query information about the user's 'state'.
Information about the user's state can also be stored using state objects in Grid and UX components.
The advantage of the state object over session variables is that the state object outlives the session. When a session expires, all of the variables in the session object are lost.
When a Grid or UX Component makes an Ajax Callback, the state object is available to any Xbasic code that handles the callback.
Using the state object also makes state information available in both client-side JavaScript code in addition to server-side Xbasic. While session variables can only be created on the server, code on both server and client can create and read state information stored in the state object for Grid and UX Components.
Storing State in Components Using the stateInfo Object
Grid and UX components provide a built-in state object that can be accessed in both server-side and client-side code to store state information. Several built-in JavaScript methods and properties are available for setting and getting state info. The .setStateInfo(obj) can be used to store state information in the component's state object. The .getStateInfo() method and .stateInfo object can be used to fetch the value of a stored item with the name ItemName. State info item names are case-sensitive in all client-side (JavaScript) code.
Storing State Info
State info can be created and stored in both client-side and server-side events and callbacks. On the client, use the .setStateInfo() method to store information the component's state object:
{dialog.object}.setStateInfo({myvar1: 'value for myvar1'});
Multiple values can be added to the state object as an object of name-value pairs. For example:
var obj = {}; obj['myvar1'] = 'value for myvar1'; obj['myvar2'] ='value for myvar2'; {dialog.object}.setStateInfo(obj);
Data that is added to the state object on the client will be made available in Ajax callbacks to the server. Most server-side component events have access to state information. The auto-generated comments in an Xbasic event or Ajax callback function created using Action Javascript include instructions for accessing and setting state information. In general, most events set state information using the e._state variable. For example:
function myCallbackExample as c (e as p) e._state.myvar3 = "value for myvar3"; ' Return optional JavaScript to execute on the client myCallbackExample = "alert("created myvar3");" end function
State information can also be set from an Xbasic script by returning JavaScript to to execute on the client. EG:
function myCallbackExample_JS as c (e as p) ' Define the javascript to execute on the client dim js as c =<<%js% var obj = {}; obj['myvar1'] = 'value for myvar1'; obj['myvar2'] ='value for myvar2'; {dialog.object}.setStateInfo(obj); %js% ' Return the javascript from the callback: myCallbackExample_JS = js end function
Reading State Info
Values in the state info object can be queried in client-side JavaScript using the getStateInfo() method or stateInfo property for the component's JavaScript object in the Grid and UX Component. The easiest way to retrieve state info is using the stateInfo property. The getStateInfo() method can be used to read state information, however getStateInfo() gets the data from the state object in the form of a text string of name value pairs delimited with & characters.
The stateInfo property, on the other hand, is a JSON object where each state info item can be referenced as if it were a property. For example:
var stateInfo = {dialog.object}.stateInfo; var myvar1 = stateInfo["myvar1"]; alert("myvar1 = " + myvar1);
Case Matters
JavaScript is a case-sensitive langauge. When retrieveing the value of an item in the state info object, the name of the item to lookup must match the case used when assigned. For example, consider the following Xbasic:
e._state.ThisIsMyVar = "some value"
When the state info object, e._state.ThisIsMyVar is added to the client-side state info object, a property called "ThisIsMyVar" is added to the component's stateInfo property. When referencing the item, the case must match exactly. EG:
var stateInfo; stateInfo = {dialog.object}.stateInfo; alert("ThisIsMyVar: " + stateInfo["ThisIsMyVar"]); // "thisismyvar" doesn't exist alert("thisismyvar: " + stateInfo["thisismyvar"]);
When this code executes, the first alert will display the message "ThisIsMyVar: some value". The second alert, however, will display "thisismyvar: undefined". This is because thisismyvar is not defined as a property of the stateInfo object.
In server-side events and ajax callbacks, state information can be read by checking the e.__si2 object in UX Components and request.variables.__si2 in Grids. State info in a component will persist beyond a session if a session ends while the user is using the component.
function myCallbackExample2 as c (e as p) dim myvar3 as C = "" ' Verify the e.__si2.myvar3 state info item exists ' before trying to assign it to the myvar3 variable. if (variable_exists("e.__si2.myvar3")) then ' It exists! myvar3 = e.__si2.myvar3 end if ' ... myCallbackExample2 = "/*optional JavaScript to return*/" end function
Storing State in Components Using Session Variables
State information can also be stored using session variables. Data stored in the session object can be accessed in any Xbasic script on the server. Session variables, however are not available on the client unless they are explicitly published. Published session variables are read-only and cannot be modified using client-side code. Further more, if a session variable changes after the component is initially rendered, the published session information on the client is not updated.
Session variables are lost when the user's session ends or the session is cleared manually using the Context.Session.Clear() method. See Understanding and Using Session Variables to learn more about working with Sessions.
Example:Using State Info to Store Default Values for New Records in a Grid with a Detail View
This example demonstrates how to store defaults for a new record in the state info object in the Grid Component with a Detail View. In the case of continuous record entry, there may be some values entered by the user in a new record that you would like to use as the default value for every new record. In this example, we capture the "lastname" entered by the user when a new record is submitted and use the value as the default value for all new records.
The "lastname" submitted for a new record can be captured in the Grid Component's AfterInsertRecord event. The e.dataSubmitted variable contains all values submitted when the user saves the new record. The code below captures the new "lastname" value and stores it in the Grid's stateInfo object. Data can be added to the state info object by setting values in the e._state object:
' Get the "lastname" value submitted by the user dim lastname as c lastname = e.dataSubmitted.lastname ' store the "lastname" in the state info object: e._state.defaultValue_LastName = lastname
Code can be added to the onInitialValueCalculate event set the default value for a new record. The onInitialValueCalculate event is triggered whenever the user clicks the "New Record" hyperlink to create a new record using the Detail View.
In the onInitialValueCalculate event, we need to get the stored lastname value from defaultValue_LastName in the state info object. The state info object is passed to the onInitialValueCalculate event through the Context.Request.Variables object: __si2. Our code needs to test that the e.__si2.defaultValue_LastName variable exists and, if it does, set the default value for "lastname" to the stored value in the state object.
' define a variable that will hold the default for "lastname" dim defaultValue as c = "" 'Any data that was stored in the stateInfo object is submitted in the __si2 object. if variable_exists("Request.Variables.__si2.defaultValue_LastName") then defaultValue = Request.Variables.__si2.defaultValue_LastName end if ' If defaultValue is not empty, set the default for "lastname" if defaultValue <> "" ' e.newvalues contains the default values for new records: e.newvalues.lastname = defaultValue end if
Videos
Check out the videos below to learn more about the Grid and UX Component's state object:
Setting State Variables in Grid and UX Components
This video is for advanced developers. It shows how "state" information can be stored in the "state" object in the Grid or UX object. The data in the "state" object can be read in both client-side and server-side events. It can also be set in either client-side or server-side events. In many cases, storing state information in the Grid or UX object's "state" object is preferable to using session variables.
See Also