Dynamic Scope
Dynamic scoping makes it possible to determine the scope of an operation or trigger when instance names are not known, and to store only DSPs that have been modified.
When developing DSPs, including contained DSPs, you may not know the actual DSP instance names that will be available at runtime. In this case, dynamic scoping enables you to:
- Package all database updates within a single transaction, preventing the possibility of data corruption
- Block only selected data in multiple components
- Minimize the amount data passed between client and server
Static Scoping Limitations
Assume that you have a DSP with several contained DSPs and you need to implement a store action that stores user modifications made in several of contained DSPs. To minimize the amount of data sent back to the server, only modified data should be stored, and to prevent possible data corruption from multiple store actions, all the data should be stored in one transaction.
If you know the names of all the contained component instances, you could declare the store operation of every contained DSP instance in the scope of the store operation of the container DSP. For example:
operation storeData scope operation Instance1.store operation Instance2.store operation Instance3.store endscope ... end; storeData
However, this includes the data of all these components, regardless of whether it has been modified, and blocks the DSPs for the duration of the transaction.
If you implement the store action of the container DSP as a weboperation or webtrigger (which are client-side JavaScript actions). Using the JavaScript API, you could check all the contained DSPs for modifications, collect the modified DSPs in a list, and loop through the list to activate the store procedure of each DSP instance, one by one. However, this requires multiple transactions.
Dynamic Scoping
In a dynamic web application, components may be
created in response to user choices, and each instance needs to be uniquely named. The available
instances and their names are not known ahead of time, so they cannot be (statically) specified in
the scope definition using operation
InstanceName.OperationName.
Dynamic scoping is achieved by having the scope declaration call a client-side callback function that returns a list of DSP instances that can be used as the InstanceName specification. For example:
operation storeData scope operation getInstances().store endscope ... end; storeData
The callback function must meet the following requirements:
- If the scope is declared for an operation or the exec operation, the callback function must be a weboperation. If it is for any other trigger, the callback function must be a webtrigger.
- It must return an array of instance names.
- Its JavaScript context must have the same
scope as the operation or trigger, meaning that the JavaScript keyword
this
refers to the same DSP instance object (for an operation) or field object (for a trigger).
Note: Nested callback is not supported. If an operation or trigger is itself a callback operation, any callback function declared in its scope block will be ignored at the runtime.
The callback function is executed by the Javascript API to determine the scope of the operation or trigger before it is executed. It does not modify the input or output scope—the operation or trigger in which the callback function is used will always be blocked by its callback function.
Dynamic Scoping
The following example implements a store routine for a DSP that has contained DSPs. For this example, assume:
-
The top-level DSP (MAIN_DSP) has:
- A contained DSP called SUB_DSP. At runtime, multiple instances of SUB_DSP can be created on the client as a result of user choices.
- A string field
MODIFIED_INSTANCES.CONTROLS.DUMMY, which is used to a list of modified SUB_DSP instance names. The
instance names are separated by a semi-colon (
;
) - A Save button (SAVE.CONTROLS.DUMMY) the implements a store routine, saving modified data from multiple component instances in a single transaction
- A webtrigger
getInstances
, defined in the Extended Triggers
- The contained DSP (SUB_DSP) has an operation
storeData()
trigger detail ; of SAVE public web scope input output operation getInstances().storeData endscope variables string vInstList, vInstName endvariables vInstList = $replace(MODIFIED_INSTANCES, 1, ";", ";", -1) forlist vInstName in vInstList newinstance "SUB_DSP", vInstName activate vInstName.storeData() endfor end
- Gather component data from the browser.
- Return data from the server.
- Include the scope of the store operations of
the DSP instances returned by the
getInstances
callback function. - Convert semi-colon separated list to Uniface indexed List.
- For each instance, ensure that the instance is created on the server. (This only needs to be done once. Subsequent requests only need to activate it.)
- For each instance, activate its
storeData
operation.
Note: The callback function
getInstances()
is implemented as a webtrigger because the store
routine is implemented in a trigger (detail trigger of the
SAVE field).
webtrigger getInstances javascript // create array of instance names var inst = this.getInstance(); var ent = inst.getEntity("CONTROLS.MODEL"); var occ = ent.getOccurrence(0); var lst = occ.getField("MODIFIED_INSTANCES"); var val = lst.getValue(); var arr = !val ? [] : val.split(";"); return arr; endjavascript end