Controlling web application state (#anchor)
With GBC in a web browser, the context/state of a program can be managed with
URL # anchors.
Purpose of #anchor in URLs
Single-page web applications (SPA) manage the state of an app by using # anchors
in the URL, such as in "http://myserver/ua/r/myApp#myState".
URL anchors allow the end user to bookmark the application in a given state.
When reloading an app by using an URL with a # anchor marker, end users expect
to land in the required application state.
# anchors usage in URLs.The URL #anchor management is only available in browser
context as there is no way for the end-user to bookmark an application in GDC, GMA or GMI.
Limitations
URL anchors management is limited to the main running application. Any application spawned using
the RUN instruction will not be able to execute these front calls or get the
applicationstatechanged special action.
Detecting #anchor changes
Changing manually the anchor in the browser address bar widget will trigger a predefined
special action named applicationstatechanged.
ON ACTION applicationstatechanged
-- get the current #anchor
-- call the corresponding function/dialogWhen using the UNBUFFERED
attribute in the input dialog, you can control the field validation with the VALIDATE action attribute. The
applicationstatechanged action is typically similar to a cancel
action, and the field validation attribute should be set to NO.
Controlling the URL #anchor
# anchor of the current
URL displayed in the browser address bar:The application code should start by a call to the getApplicationState
frontcall, to check for an existing #anchor. If an anchor is
present, the program flow should go to the corresponding function/dialog. When returning from this
function, the code should execute a dialog as when no anchor was provided.
In the initial/main dialog (and if needed, in subsequent dialogs), implement an ON
ACTION handler for the applicationstatechanged action name, to detect when
the end user changes the #anchor part of the URL. If this action
is triggered, query the anchor with the getApplicationState frontcall and follow
the same work flow as when the application starts.
In all interactive code parts that correspond to an application state which can be reached
directly by using a #anchor in the URL, set the current #anchor
with the setApplicationState frontcall, before starting a new dialog
instruction.
Example
In the following example, the initial state of the application is a regular
MENU, as well as en INPUT BY NAME and CONSTRUCT
dialog, that can be reached with the #input and #construct anchors in the URL.
LAYOUT
GRID
{
Num: [f1 ]
Name: [f2 ]
}
END
END
ATTRIBUTES
EDIT f1 = FORMONLY.num TYPE INTEGER;
EDIT f2 = FORMONLY.name TYPE VARCHAR;
ENDMAIN
DEFINE anchor STRING
OPTIONS INPUT WRAP
OPEN FORM f1 FROM "form"
DISPLAY FORM f1
CALL ui.Interface.frontCall("browser",
"getApplicationState",[],[anchor])
ERROR "Initial anchor: ", NVL(anchor,"none")
LET int_flag = FALSE
WHILE NOT int_flag
LET anchor = switch_context(anchor)
END WHILE
END MAIN
FUNCTION switch_context(anchor STRING) RETURNS STRING
DEFINE next_anchor STRING
CASE anchor
WHEN "input"
CALL do_input()
LET next_anchor = NULL
WHEN "construct"
CALL do_construct()
LET next_anchor = NULL
OTHERWISE
IF anchor IS NULL THEN
LET next_anchor = do_menu()
ELSE
ERROR "Invalid application state: ", anchor
END IF
END CASE
RETURN next_anchor
END FUNCTION
FUNCTION do_menu() RETURNS STRING
DEFINE anchor STRING
CALL ui.Interface.frontCall("browser",
"setApplicationState",[NULL],[])
MENU "test"
ON ACTION input
CALL do_input()
LET anchor = NULL
EXIT MENU
ON ACTION construct
CALL do_construct()
LET anchor = NULL
EXIT MENU
-- During this menu dialog, user can switch to another app state
ON ACTION applicationstatechanged
CALL ui.Interface.frontCall("browser",
"getApplicationState",[],[anchor])
ERROR "State change: ", anchor
EXIT MENU
ON ACTION quit
LET int_flag=TRUE
EXIT MENU
ON ACTION close
LET int_flag=TRUE
EXIT MENU
END MENU
RETURN anchor
END FUNCTION
FUNCTION do_input() RETURNS ()
DEFINE rec RECORD
num INTEGER,
name VARCHAR(50)
END RECORD
CALL ui.Interface.frontCall("browser",
"setApplicationState",["input"],[])
INPUT BY NAME rec.* ATTRIBUTES(UNBUFFERED)
LET int_flag=FALSE
END FUNCTION
FUNCTION do_construct() RETURNS ()
DEFINE sqlcond STRING
CALL ui.Interface.frontCall("browser",
"setApplicationState",["construct"],[])
CONSTRUCT BY NAME sqlcond ON num, name
LET int_flag=FALSE
END FUNCTION