skip to main content
OpenEdge Development: ADM and SmartObjects
SmartLinks : Invoking behavior in other linked objects
 
Invoking behavior in other linked objects
A SmartObject can execute internal procedures or functions in any SmartObject to which it is linked. This often is necessary to access special behavior or to facilitate communication between the SmartObjects. Accessing these internal procedures or functions does not require the SmartObjects to know each others’ procedure handles.
The ADM accomplishes this using the Progress PUBLISH/SUBSCRIBE syntax. Each SmartLink represents a set of named events. During the creation of a SmartLink, its source automatically subscribes to the named events in the target procedure, and vice versa. For example, the Navigation-Target subscribes to the named events fetchFirst, fetchNext, fetchPrev, and fetchLast in a SmartPanel Navigation-Source. When the application runs and a user presses the Next button, the SmartPanel executes the following statement, which sends a message to its Navigation-Target:
 
PUBLISH ’fetchNext’.
This syntax does not require the SmartPanel to keep track of which objects are its Navigation-Targets. Instead, the Progress interpreter tracks this using the SUBSCRIBE statements that the addLink procedure executes on the Navigation-Target’s behalf. (The addLink procedure is executed when the SmartObjects are initialized.)
The standard ADM code contains many such PUBLISH statements. Custom application code can also use PUBLISH statements such as these to communicate between SmartObjects. For example, suppose that you use the addLink procedure as follows to add a SmartLink that is not recognized as one of the object’s supported links:
 
RUN addLink(h1, ’newLink’, h2)
The addLink procedure now executes the following statement:
 
SUBSCRIBE PROCEDURE h2 TO ’newlink’ IN h1.
The h1 procedure can now execute the PUBLISH statement whenever it wants to invoke the named event in h2:
 
PUBLISH ’newlink’.
Each SmartObject includes properties for each link type. These properties, which have names composed of the link type followed by an event name, define the events to which an object is automatically subscribed by the addLink procedure. (See Table 3–2 through Table 3–7 for lists of source and target events.) You can modify these properties for your custom link types.
Pass-through links
Applications invariably contain SmartObjects that are subordinate to SmartContainers, because either they are contained by them (a SmartFrame inside a SmartWindow or another SmartFrame) or they are lower in the SmartContainer hierarchy (a SmartWindow defined as the logical child of another). In some cases, you must be able to connect a SmartObject outside the containing or parenting SmartContainer with its subordinate SmartObject, even though you have direct access only to the SmartContainer. A SmartObject inside a SmartContainer cannot link directly to a SmartObject external to the SmartContainer at design time; however, you can create a two‑step link called a pass‑through link that allows the two objects to communicate. In a pass‑through link, the outer SmartObject is the source for the containing or parenting SmartContainer and the containing or parenting SmartContainer is the virtual source for the contained SmartObject.
Figure 3–1 illustrates the two‑step pass‑through link.
Figure 3–1: Pass‑through link
Example of setting up a pass-through link
Suppose you build a special SmartFrame, called an Order SmartFrame, containing these SmartObjects:
*A SmartDataObject for Orders
*A SmartDataBrowser that browses this SmartDataObject’s query
*A SmartDataViewer that displays field values for the current Order
*An Update SmartPanel that allows changes to the current Order record
You create the Order SmartFrame, as a package; that is, as a single reusable object. You want to use an instance of this SmartFrame to display and update Orders for a particular Customer, an operation that requires passing Customer keys (the Cust-Num fields) from a SmartObject outside the SmartFrame to the SmartDataObject it contains. Your application will have as its basis a SmartWindow that will contain an instance of your special SmartFrame and a SmartDataObject and SmartDataBrowser for Customers. You will link the SmartObjects in the SmartWindow, so each time an application user selects a Customer, the SmartDataObject inside the Order SmartFrame receives the Cust-Num value and reopens its query for Orders of that Customer; that is, the Order SmartDataObject will be the Data-Target of the Customer SmartDataObject. Because the containing SmartFrame is between these SmartDataObjects, you cannot link them directly and must establish a pass‑through link. In one part of this link, the Order SmartFrame is the virtual Data-Source for its Order SmartDataObject. In the other part of this link, the Customer SmartDataObject is the Data-Source, and the Order SmartFrame is its Data-Target.
You must build the your application in a specific order:
1. Build the inner connection, between the SmartFrame and the contained SmartDataObject.
2. Create the SmartWindow and define its contents.
3. Build the outer connection, between the outer SmartDataObject and the SmartFrame.
At run time, the ADM combines the two separate connections into a single link.
When you build your Order SmartFrame, you define the SmartFrame as the virtual Data-Source for the SmartDataObject that it contains in the Link Editor. To do this, you select THIS-PROCEDURE (which indicates the SmartFrame container you are building) as the Data-Source and the Order SmartDataObject as the Data-Target. You then tell the Order SmartDataObject to expect a Cust-Num key from its Data-Source at run time. The Link Advisor in the AppBuilder cannot do this automatically, because the objects are not in a single SmartContainer; therefore, you must set the ForeignFields property of the Order SmartDataObject so it expects Customer keys. To do this, define a createObjects procedure in the SmartFrame with this code:
 
RUN SUPER.
/* h_order is a handle to the Order SmartDataObject */ 
DYNAMIC-FUNCTION(’setForeignFields’ IN h_dorder,"Order.Cust-num, Cust-Num").
The RUN SUPER statement runs the standard behavior for createObjects. This runs the AppBuilder‑generated adm-create-objects procedure, which creates all the objects in the SmartFrame and establishes the necessary links. After the standard createObjects procedure executes, the setForeignFields function tells the SmartDataObject to expect to receive the Cust-Num field from its Data-Source and to use it to match the Order.Cust-Num field in its own query.
Figure 3–2 illustrates the established inner connection.
Figure 3–2: Pass‑through link: inner connection
You now create a SmartWindow into which you place a Customer SmartDataObject and SmartDataBrowser. Next, drop your Order SmartFrame into the SmartWindow. You want to pass Customer keys to that SmartFrame, so they get to the Order SmartDataObject inside it. You also do this in the Link Editor, by choosing the Customer SmartDataObject as the Data-Source and the SmartFrame as its Data-Target. Theoretically, when you run this SmartWindow, you have two Data links, one from the Customer SmartDataObject to the SmartFrame and one from the SmartFrame to the Order SmartDataObject, as shown in Figure 3–3.
Figure 3–3: Pass‑through link: inner and outer connections
The SmartFrame, however, is not prepared to deal with the Customer keys; the two Data links must be combined into one, so the Customer SmartDataObject and the Order SmartDataObject can communicate directly with each other. To accomplish this, the addLink procedure, which establishes links, notes that the SmartFrame container is both a Data-Target and a Data-Source (this makes the Data link a potential pass‑through link) and modifies the inner link by changing its Data-Source from the SmartFrame to the Customer SmartDataObject. There is now a link from the Customer SmartDataObject directly to the Order SmartDataObject, and the two objects can function as if they are in the same container. With a pass‑through link, it is the link rather than the data that is passed through.
The outer link, from the Customer SmartDataObject to the SmartFrame, is maintained. This enables the code in the SmartFrame to use Customer keys passed from the Customer SmartDataObject. There is no standard code in the SmartFrame object or its super procedure containr.p to handle the data. If desired, you can write application code to respond to events from the Customer SmartDataObject.
Getting records from an additional source
As with most other link types, it is valid for a Data-Target SmartObject to have only one Data-Source at a time. Sometimes, however, you must be able to pass a record down a different path than the normal Data link path. For example, suppose you have a a SmartDataViewer that receives Customer values from a SmartDataObject. When the SmartDataObject is notified of the dataAvailable event, it gets the Customer values for the current row in the Customer SmartDataObject. This sequence is repeated whenever the SmartDataObject retrieves a new Customer record. Occasionally, however, the SmartDataViewer might need a different record. For example, it might need the User record on startup, to perform security checks. This User record might not be available through its normal Data-Source.
You can deal with this scenario in several ways. In addition to the default automatic execution of the colValues function in the Data-Source from dataAvailable, you can program a SmartObject to run colValues in a SmartObject other than its regular Data-Source. This approach provides an ADM-standard way to get an extra set of values from one SmartObject to another. For example, you could define an initializeUser link from the source of the User ID to the SmartDataViewer that needs it. When you create a nonstandard link, Progress performs a single SUBSCRIBE in the target for the link name. For example, if the initializeUser procedure is defined in the SmartDataViewer, it will be executed whenever the source PUBLISHes the initializeUser event. The User ID can be passed as an argument or queried from target to source.