Working with Dynamic Web Objects

Meaningful URLs and History Management

Studio Support for Web Application Development

Introduction

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.

Definitions

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.

Creating Dynamic Objects

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.

Creating a Dynamic Object Container

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.

Defining Object Behavior

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.

Creation Phase

Registering a new Dynamic Object

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.

Setting Dynamic Properties

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.

Displaying Dynamic Objects

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.

Modification Phase

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 New Dynamic Objects

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.

Inserting New Dynamic Objects at a Specific Location

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.

Prepending New Dynamic Objects

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.

Appending New Dynamic Objects

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 Existing Dynamic Objects

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.

Moving Existing Dynamic Objects to a Specific Location

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.

Destroying Existing Dynamic Objects

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.

Using Dynamic Objects

Getting a Dynamic Object

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.

Getting and Setting Properties

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.

When to Load Dynamic Objects?

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

 

See Also

Developing Web Applications