In this section, we will explain how dynamic objects function and how you can integrate them into your own applications. Please read this section entirely, as there are some crucial steps in the process of using dynamic objects that slightly differ from standard DataFlex practices.
We will start by introducing new terminology for dynamic objects. This terminology will be used throughout the documentation, as well as in DataFlex Learning Center videos and other presentations.
The process of creating dynamic objects in a web application contains the following steps:
1. Create an instance of cWebDynamicObjectContainer.
2. Define object-specific behavior (functions and procedures)
3. Creation Phase
4. Activation of the container
5. Modification Phase
The following subsections will elaborate on these five steps. Small code snippets are included to hopefully paint a clearer picture of how to use this library in practice.
Dynamic objects exist inside a specialized container: the cWebDynamicObjectContainer. Creating an instance of this object works just like instantiating any other type of object. The class functions like a cWebGroup and therefore supports basic container properties such as pbScroll and piColumnCount. Listing 1 shows an example of a cWebDynamicObjectContainer instance. Please note that the container itself should not contain any static child objects: these will be added at runtime.
Object oContainer is a cWebDynamicObjectContainer
Set piColumnSpan to 12
Set piColumnCount to 12
End_Object
Listing 1: Creating an instance of cWebDynamicObjectContainer.
Not every dynamic object behaves the same. Suppose you have two buttons that react differently when being clicked by the user. They may look the same, but their behavior is not. Due to the nature of the DataFlex language, it is not possible to dynamically define functions and assign them to classes or objects. Therefore, in order to define object-specific behavior for dynamic objects, you have to create a subclass of the object you wish to implement create dynamically. You then use this subclass to outline the specific behavior the dynamic object should have. Later, dynamic objects will be created according to this subclass definition and thus inherit the behavior. Inside the subclass you can also define new properties. All dynamic objects of that subclass will support the new properties.
Listing 2 contains a subclass definition for a button with a single new property called piCounter. This is achieved by simply extending the existing cWebButton class.
Class cMyWebButton is a cWebButton
Procedure Construct_Object
Forward Send Construct_Object
{ WebProperty=Client }
Property Integer piCounter 0
Set piColumnSpan to 8
End_Procedure
End_Class
Listing 2: An example subclass of cWebButton.
Now that the dynamic container is set up and the dynamic objects have a definition, dynamic objects can be registered. This phase is called the creation phase. Listing 3 shows how this should be done. Simply use the CreateDynamicObject function, defined in the cWebDynamicObjectContainer class, to register a dynamic object. This procedure has the following signature:
Procedure CreateDynamicObject Integer iClassId String sDynamicObjectId String sParentId Returns Handle.
The parameter iClassId refers to the Class ID of the class that the object should be an instance of. It is often useful to use the RefClass function here. Also note that sDynamicObjectId must be a unique identifier for the dynamic object, meaning a single dynamic container cannot contain two objects with the same ID. If an object is located at the root of the dynamic container (that is, it has no dynamic parent) String sParentId should be an empty string, like in Listing 3.
Get CreateDynamicObject of oContainer (RefClass(cMyWebButton)) "oMyWebButton" "" to hoObj
Listing 3: Registering a dynamic object.
Please note that during the creation phase all dynamic objects you create will be inserted sequentially. That means that if you want object A to be displayed before object B (under the same parent), it should be created first.
By using dynamic properties, it is possible to create multiple dynamic objects based on the same subclass that each have a distinct properties. For regular DataFlex objects, properties are often defined inside the Construct_Object procedure or during the actual object creation. This is not possible for dynamic objects due to their dynamic nature. Their property values might not be known at compile time. The Dynamic Object Library supports dynamic properties to solve this issue. Dynamic properties contain property values that are different from the default values set in the subclass definition. The procedure used to set a dynamic property value is Procedure InitDynamicProp String sPropName Variant vPropValue, defined in the cWebObject class.
Listing 4 demonstrates how to create two buttons based on the same subclass, but with different captions. Both buttons will be created from the class definition cMyWebButton we created earlier, but their properties will differ.
Get CreateDynamicObject of oContainer (RefClass(cWebGroup)) "oGroup1" "" to hoGroup1
Send InitDynamicProp of hoGroup1 "piColumnCount" 8
Get CreateDynamicObject of oContainer (RefClass(cMyWebButton)) "oButton1" "" to hoButton1
Send InitDynamicProp of hoButton1 "psCaption" "Button 1"
Get CreateDynamicObject of oContainer (RefClass(cMyWebButton)) "oButton2" "" to hoButton2
Send InitDynamicProp of hoButton2 "psCaption" "Button 2" Listing 4: Each dynamic object can have different initial property values.
The final step in the process of creating dynamic objects is rendering the objects. To do this, simply call Procedure Activate, included in the cWebDynamicObjectContainer class. This function will send the contents of the dynamic container to the client, which will in turn render all the objects. The example written in Listing 4 results in the interface shown in Figure 1. The two buttons in Figure 1b are generated at runtime by clicking the Load Dynamic Objects button and will follow the behavior defined previously.
At this point, the dynamic container is activated and its contents are shown to the user. It is possible that the previously defined structure of the dynamic objects needs to be updated or perhaps new dynamic objects need to be registered and rendered. This all happens during the modification phase. Currently, the library only supports inserting new dynamic objects. Moving or destroying existing dynamic objects is a planned feature.
Inserting a new dynamic object is fairly similar to registering one in the creation phase. First, the dynamic object needs to be registered and created using the method explained in section 4.3.1. Then, again comparable to before, the dynamic properties should be set. At this point the object is ready to be inserted. If it has child objects, these should also be created now. Please be aware that only a single dynamic object (and all its children) can be inserted at once. This means that in order to insert multiple dynamic objects that are not nested inside each other, the procedure has to be called multiple times.
This procedure is used to insert a new dynamic object:
Procedure InsertAfter String sDynamicObjectId String sInsertAfter
...and is implemented in the cWebDynamicObjectContainer class. As its name suggests, this function inserts an object after another object. Listing 5 shows how a new cWebGroup object can be inserted after the previously created web group.
Get CreateDynamicObject of oContainer (RefClass(cWebGroup)) "oGroup2" "" to hoGroup2
Send InitDynamicProp of hoGroup "piColumnCount" 8
Get CreateDynamicObject of oContainer (RefClass(cWebForm)) "oForm" "oGroup2" to hoForm
Send InitDynamicProp of hoForm "piColumnSpan" 8
Send InitDynamicProp of hoForm "psLabel" "Test Input"
Send InsertAfter of oContainer "oGroup2" "oGroup1"
Listing 5: New dynamic objects can be inserted into the object structure at runtime.
There might be cases where you want to insert a newly created dynamic object at a very specific location, for instance as the fifth element in a list of other elements. For these cases, the Dynamic Objects library supports the following procedure:
Procedure InsertDynamicObjectAtIndex String sDynamicObjectId String sParentId Integer iInsertIndex
Please keep in mind that this procedure is slightly more dangerous to use, because the framework assumes you know what you are doing. In most cases,usage of InsertDynamicObject should suffice.
In some cases, new objects should always be inserted at the front of a list or queue. In that case, a procedure that will always prepend a dynamic object to its parent’s list of children can be particularly useful. The cWebDynamicObjectContainer class implements the following procedure:
Procedure PrependDynamicObject String sDynamicObjectId
The demo application Simple Widgets uses this procedure to prepend a new widget object.
Next to prepending, it is also possible to append new dynamic objects at the end of their parent’s list of children. The following procedure will always insert a new dynamic object after all other dynamic objects that its parent has:
Procedure AppendDynamicObject String sDynamicObjectId
The demo application Simple Widgets uses this procedure to append a new widget object.
Moving dynamic objects follows roughly the same workflow as insertion. The cWebDynamicObjectContainer class contains a procedure with the following interface:
Procedure MoveDynamicObject String sDynamicObjectId String sInsertAfter
The MoveDynamicObject procedure requires the object that is being moved, to already exist inside the container. Furthermore, the moved object inherits the parent structure of the object it is moved after.
Code listing 6 shows how to move the dynamic object called "Group 1" to after the object called "Group 2", which was created in section 4.4.1. Currently, "Group 1" is positioned before "Group 2".
Send MoveDynamicObject of oContainer "Group 1" "Group 2"
Listing 6: Dynamic objects that have been created already can be moved around inside the container.
To allow for more complex operations, it is also possible to move a dynamic object to a specific index in its parent’s list of children. This is especially useful in cases where you manually keep track of locations of objects inside a certain list. Please note that inserting at an index that already holds a dynamic object will shift the existing object to the next index. The cWebDynamicObjectContainer class contains the following procedure to support this operation:
Procedure MoveDynamicObjectToIndex String sDynamicObjectId String sParentId Integer iInsertIndex
The demo application Questionnaire uses this procedure to allow a user to change the order in which questions appear.
Destruction of a dynamic object means completely removing it from the container. This includes removal of all its properties and children. The cWebDynamicObjectContainer class contains the following method to destroy a dynamic object:
Procedure DestroyDynamicObject String sDynamicObjectId
Please note that destroying a dynamic object automatically destroys the object’s children as well.
After having created and registered dynamic objects they can be used in your application. In a regular application, one would execute a method of an object by simply writing Send ProcedureName of hoObj. In the case of dynamic objects there must be one extra intermediate step. Dynamic objects are not always present in the memory of the web server due to process pooling. Therefore, the actual object (hoObj in this example) on which you want to call a procedure should be retrieved from the dynamic container first. The cWebDynamicObjectContainer class contains a function to do this.
Function DynamicObject String sDynamicObjectId
By providing the function with the ID of the dynamic object, it will return the handle of the object. Listing 7 shows how to use this in practice. If the object is not yet present in memory, e.g. due to a different process handling the current request, the Dynamic Object Library creates the object according to the stored definition. If the object was already created by this server process, it will simply fetch the handle.
Get DynamicObject of oWebDynamicObjectContainer "oMyWebButton" to hoButton Send DoStuff of hoButton
Listing 7: Executing actions with or on dynamic objects requires one extra step.
Dynamic objects have been implemented in such a way that getting and setting properties remains the same. When using dynamic objects in an application, one can simply use WebGet and WebSet commands to get and set property values. The framework will handle synchronization between the server and the client. Listing8extendstheprevioussubclass definition of cMyWebButton with an OnClick procedure that increments a synchronized property.
Class cMyWebButton is a cWebButton
Procedure Construct_Object
Forward Send Construct_Object
{ WebProperty=Client }
Property Integer piCounter 0
Set piColumnSpan to 8
End_Procedure
Procedure IncrementCounter
Integer iCounter
WebGet piCounter to iCounter
Increment iCounter
WebSet piCounter to iCounter
End_Procedure
Procedure OnClick
Send IncrementCounter
End_Procedure
End_Class
Listing 8: An example subclass of cWebButton containing operations on its properties.
Dynamic objects can be loaded at any moment. Simply follow the steps laid out previously to load dynamic objects into your application. If you want your dynamic objects to show up as soon as a view is loaded, there are a few things you can do.
If you are using a drilldown theme style, define the creation of your dynamic objects inside Procedure OnBeforeShow, defined on the cWebView class. If you are using a regular theme style, you can simply define the creation of your dynamic objects inside Procedure OnLoad of any of your regular web objects.
Previous Topic: Meaningful URLs and History Management
Next Topic: Studio Support for Web Application Development