A Web Application works differently than the more traditional desktop application. It is very important to understand that the client (your browser) does not own a single instance of a WebApp (WebApp.exe). There is no persistent connection between the browser and server application. Instead, you have a pool of WebApp processes waiting to be used. When a client needs to use one of these processes, it connects to it, it initializes the process and synchronizes it with the data from the client, it works with the process to handle whatever requests are needed and then it disconnects from the process. This process is returned to the application pool where it waits for another client request.
When your Web Application starts a number of WebApp.exe processes are loaded and initialized. The number of processes created depends on your process pooling settings (and you do want to use process pooling). This startup creates all of your objects, sets all of your properties and is then added the process pool.
Here are some things to remember about this initialization process:
During this initialization you are not connected to a client. You are starting a default instance that might be used by any client and might be used by multiple clients.
Because you are not connected to the client you should not be setting web-properties using WebSet and WebGet. Instead you should use Get and Set. Get and Set represent the default values of the properties and these are the defaults that apply to all client instances that use these values.
If you attempt to WebSet a property during initialization you will receive a runtime error.
If you attempt to WebGet a property during initialization, it will act as if you used Get and return the default value. Be careful when using WebGet during initialization – normally you want Get.
If an error occurs during initialization there is no client/browser there to handle the error. Therefore such an error is considered to be fatal. If you are running under the debugger you will see the error in the debug’s error display. If a startup error occurs at deployment, the error is logged in the Windows event log.
Remember that every object you create and every property you set is defining the default environment that all clients will use when they first connect to the application. As changes are made, those changes are stored as web-properties, which are maintained on the client and synchronized on the server as needed.
The URL you enter from your browser loads a fairly simple page (e.g., http://MySite/MyApplication/Index.html). This page contains the JavaScript classes needed to run the application. It also implements an AJAX style request handler. From this point on, your web page is never reloaded and all communication occur via AJAX style requests. These requests do all the work. They allow you to create a new browser session, display your main application, load views and dialogs, and interact with those views and dialogs.
This request process occurs through web-service calls. The data is passed and returned in JSON format. This response/request cycle is sometime referred to making a “round trip to the server”. You can actually view each request in a browser’s debugger. If you are using FireFox and you have downloaded the Firebug debugger, you can activate the debugger and view the “console”, which lets you see each request/response.
These requests can be used for all kinds of purposes but some the standard requests are:
Load your WebApp – This starts a new session on the browser and displays your application shell (Menus, Toolbars, Logins and possibly an initial view). From the user’s point of view, this starts the application.
Load a New View/Dialog – This occurs when a show view/dialog request is made for a view/dialog that has not yet been loaded and initialized on the client. The server must send a definition of the view back to the client so it can show it.
Show a View/Dialog – This occurs when a show view/dialog request is made for a view/dialog that it already loaded in the client. In most cases, the client can handle this change without a server request. You can tell the client to send a server request anytime you hide or show a view or dialog.
Interact with a View/Dialog – this occurs when a client action requires server interaction. Examples of this are finding, saving or deleting records. Often this involves just working within a single view.
Before discussing these individual request actions, we will discuss what is common for all of these requests:
Remember that each request attaches to the next process in the pool. Never assume that you have a persistent one-to-one connection between your browser and an instance.
Therefore each request must initialize the instance based on the data passed from the browser. The framework does this for you. It synchronizes the values of all web-properties. Within views and dialogs the state of all DDO structures and all controls are synchronized during this process.
Requests are serviced by calling server side events (OnClick) or server actions (RequestSave).
These handlers contain the code or call other methods that contain the code that handles a request.
The code inside of these handlers (and the methods they call) will primarily interact with web-properties. These are accessed through WebGet and WebSet and not Get/Set.
Some server side events are optional and can be enabled or disabled using the pbOnServerEventName property (e.g., OnShow and pbServerOnShow).
If there are no server required events, it is possible that major operations can occur without a server request. For example, if you wish to redisplay a view and pbOnServerShow is set False on the new view and pbServerOnHide is set False on the old view, the view change may occur without any server request.
An attach and detach event, OnAttachProcess and OnDetachProcess, is sent with every request. These are low level events that are triggered by the low level attaching and detaching of a process. These should not normally be augmented. The attach occurs before any synchronizations and the detach occurs after the response has been built. Therefore you should not use WebGet or WebSet within these events. These are so low level, that they will not be discussed with the other processing and events. Just remember that these are the first and last things that happen and that you normally will not use them.
When a request occurs only the views and dialogs that are part of the request are synchronized on the server. This may be a single view, two views (one view switching to another) or a view containing a cascade of modal dialogs. The framework determines which views and dialogs need to be synchronized.
Most requests are sent to a view or a dialog and they look like this:
OnAttachProcess is sent to cWebApp Object
Synchronize all web-properties
OnSyncWebApp is sent to your cWebApp Object
For each view that needs to be synchronized
Get AllowAccess
Rebuild_Constraints is sent to all DDOs in the main DDO structure
Synchronize the main DDO structure.
OnSyncView
Process all request actions
Build response data
OnDetachProcess is sent to cWebAppObject
OnSyncWebApp is sent to your main cWebApp object. This can be used to handle any special customizations based on the web properties. This can be used to handle any special user rights considerations. This is sent before the view has been synchronized.
If a view’s AllowAccess returns False the request is canceled. This is security feature.
OnSyncView is sent to each view and dialog used in this request. This can be used to handle any special customizations based on the web properties. This can be used to handle any special user rights considerations. This is also used to define any custom dynamic constraints in your view’s DDO structure. Typically this involves defining your DDO OnConstrain methods based on values of web-properties.
After OnSyncView is called, Rebuild_Constraints is sent to your DDO structure. Therefore if constraint customizations occur you do not need to manually rebuild constraints.
The “process all request actions” step may be the calling of actions such as loading a view or hiding a view. These actions may cause other events to fire. A request action may also trigger an event directly (e.g. OnClick).
Normally you constraints can be customized as part of the rebuild_constraints process. This will call any custom code within your DDO OnConstrain events, which is usually all you need. If you change your constraints anywhere else, such within OnSyncView you will need to remember to send Rebuild_Constraints to your DDOs.
You should test the value returned by AppSynching inside any data dictionary events you implement that could be triggered during a find operation (e.g. OnPostFind, Refresh, OnNewCurrentRecord). The AppSynching function returns true while the framework is synchronizing web properties and the view's DDO structure, and returns false while processing all request actions. Normally you do not want to process DDO events when AppSynching returns true.
This first time a client requests a WebApp, the WebApp itself must be loaded onto the client. Here is an outline of what happens:
CreateSession – create a new session
Clear all web properties
Send OnChangeRights to the cWebApp object and to the command bar and all of its objects.
Serialize all web-objects to be displayed (this will be sent to the client and converted to client side objects). Typically this consists of the main WebApp object, the menus and toolbars.
Send the OnLoad to every web-object that is serialized.
If a default login or default view is defined load it (See Action - Loading a View/Dialog)
OnChangeRights is sent during this load. This event will also be sent when a users right’s change – typically after a login. This is sent to every menu object and, based on user rights, can be used to enable or hide items, toolbars, menus or even the entire commandbar system.
OnLoad is sent when a web-object is loaded. This is sent for every web-object. It is usually only sent once during a session.
The first time a view or dialog is requested the view’s object definition must be serialized and sent to the client. The client will use this information to show these controls on the browser. Once loaded, the client can hide and reshow these views without needing to request these definitions again.
Often when displaying a view, an existing view will be hidden and the new view will be displayed. In the case of modal dialogs a new dialog will be loaded on top of the old view or on top of another. In both cases, the old view and the new view or dialog must both be fully synchronized. Here is an outline of what happens
Get AllowAccess of View
Send Clear_All to the view’s main_DDO
OnSyncView
Serialize all web-objects to be displayed (this will be sent to the client and converted to client side objects).
Send the OnLoad to every web-object that is serialized
OnHide is sent to the old view (if there is an old view)
OnShow is sent to the new view
If a view’s AllowAccess returns False the request is canceled. This is security feature.
OnHide is sent if the old view’s pbServerOnHide is True
OnShow is sent if the new view’s pbServerOnShow is True
OnLoad is sent when a web-object is loaded. This is sent for every web-object. It is usually only sent once during a session.
If there was on old view, that view will have already been synchronized as part of the regular request synchronization process.
OnHide is sent to the old view (if there is an old view)
OnShow
OnHide is sent if the old view’s pbServerOnHide is True
OnShow is sent if the new view’s pbServerOnShow is True
If these views are already loaded both the old and new views will have already been synchronized as part of the regular request synchronization process
If pbServerOnHide and pbServerOnShow are false for their respective objects, there may be no server request at all.
This is sent to every web-object when the object is loaded. If a view or dialog or WebApp has not yet been requested its object definition and every web object it contains must be serialized and sent to the client, where the client can display it. OnLoad is sent to each object as it is serialized. This load process usually only occurs once per session.
OnLoad is sent unconditionally – there is no pbServerOnLoad.
This is a good event to augment at the view level when you want to view to appear with a default record.
Procedure OnLoad
Send Find of OrderHea_DD FIRST_RECORD Index.1
End_Procedure
This can also be useful to handle first time processing when loading a WebApp. For example you could augment OnLoad in your cWebApp object to perform an automatic login.
Procedure OnLoad
Boolean bOk
Get UserLogin of ghoWebSessionManager "guest" "Guest" to bOk
End_Procedure
OnShow is sent to a view or dialog when it is being shown. This is sent when the view is first loaded and each time it is re-displayed. OnShow is only called if pbServerOnShow is True.
OnShow is useful with Modal Dialogs, which often require that you perform initializations each time the dialog is shown.
OnHide is sent to a view or dialog when it is being hidden. This is sent when another view is shown or when a Modal Dialog is closed. OnHide is only called if pbServerOnHide is True.
OnChangeRights is sent whenever there is a change in user rights. It is sent to the cWebApp object, the command bar object and all objects within the command bar.
When rights change, OnChangeRights is not sent to views. It is expected that you will use OnSyncView to handle any special view roles and rights requirements.
OnSyncWebApp is sent to the cWebApp object with each request. It is sent after the web-properties have all been synchronized but before any view DDOs have been synchronized. This can be used for special global initializations and is useful for setting any special rights or roles.
OnSyncView is sent to each view that needs to be synchronized during a request. It is sent after the web-properties, DDOs and DEOs are synchronized.
This is an important event. You can use this to control user rights and to disable or hide controls within a view as needed. Remember to use WebGet and WebSet for this.
You can also use this to initialize custom constraints. If you change constraints you must remember to send Rebuild_Constraints to the DDO.
Here are two ways you might customize DDO constraints. This first method uses OnSyncView to WebGet three web-properties and to set these to regular DDO properties. When Rebuild_Constraints gets called, OnConstrain will be applied and the filter will be in place:
Object oCustomerGrid is a cWebView
{ WebProperty = True }
Property String psFilter ""
{ WebProperty = True }
Property String psFilterFrom ""
{ WebProperty = True }
Property String psFilterTo ""
Procedure OnSyncView
String sFilter sFrom sTo
WebGet psFilter of oCustomerGrid to sFilter
WebGet psFilterFrom of oCustomerGrid to sFrom
WebGet psFilterTo of oCustomerGrid to sTo
Set psType of oCustomer_DD to sFilter
Set psFrom of oCustomer_DD to sFrom
Set psTo of oCustomer_DD to sTo
Send Rebuild_Constraints of oCustomer_DD
End_Procedure
Object oCustomer_DD is a Customer_DataDictionary
Property String psType
Property String psFrom
Property String psTo
Procedure OnConstrain
String sFilter sFrom sTo
Forward Send OnConstrain
Get psType to sFilter
Get psFrom to sFrom
Get psTo to sTo
If (sFilter = "NUMBER") Begin
Constrain Customer.Customer_Number Between sFrom and sTo
End
If (sFilter = "NAME") Begin
Constrain Customer.Name Between sFrom and sTo
End
End_Procedure
End_Object
...
This next method does this same thing just using web-properties. In this case there is no OnSyncView, because OnConstrain use web-properties and WebGet directly. Even with no OnSycView augmentation, Rebuild_Constraints will send OnConstrain, which will do the work for you.
Object oCustomerGrid is a cWebView
{ WebProperty = True }
Property String psFilter ""
{ WebProperty = True }
Property String psFilterFrom ""
{ WebProperty = True }
Property String psFilterTo ""
Object oCustomer_DD is a Customer_DataDictionary
Procedure OnConstrain
String sFilter sFrom sTo
Forward Send OnConstrain
WebGet psFilter of oCustomerGrid to sFilter
WebGet psFilterFrom of oCustomerGrid to sFrom
WebGet psFilterTo of oCustomerGrid to sTo
If (sFilter = "NUMBER") Begin
Constrain Customer.Customer_Number Between sFrom and sTo
End
If (sFilter = "NAME") Begin
Constrain Customer.Name Between sFrom and sTo
End
End_Procedure
End_Object
...
Previous Topic: Web Properties Methods and Events
Next Topic: Positioning and Layout of Controls