Release 10.1A: OpenEdge Development:
ProDataSets

> ## Setting up an event handler for the FIND-FAILED buffer event

> We can extend the example even further to show how to use the `FIND-FAILED` event. This ProDataSet buffer event is similar to the `OFF-END` event on a query in that it gives your application the opportunity to retrieve missing data without the rest of the application even being aware that it wasn’t in the ProDataSet in the first place. You can use the event to retrieve needed rows one at a time, or in batches, depending on the situation.

> | To show how the event works:

> 1. Add a callback for a `FIND-FAILED` event handler to the `LEAVE` trigger for `SalesRep` in `PickOrderBatch.w`, right after the callback for the `OFF-END` event. For example:

>     /* Set up an OFF-END event handler for the Order buffer to do batching. */
>     QUERY OrderBrowse:SET-CALLBACK-PROCEDURE("OFF-END","OffEndOrder",
>                                              THIS-PROCEDURE).
>     /* Also a FIND-FAILED event handler for the Item table. */
>     BUFFER ttItem:SET-CALLBACK-PROCEDURE("FIND-FAILED","FindFailedItem",
>                                          THIS-PROCEDURE).

> Note that the `OFF-END` event must be attached to a query, and the `FIND-FAILED` event to a ProDataSet buffer, in this case the `ttItem` buffer, which will allow the procedure to retrieve `Items` to add to the browse the first time they are referenced.

> 2. Change the call to `fetchOrderDetail` in the `MOUSE-SELECT-DBLCLICK` trigger for the `Order` browse as you have done in another variation of this procedure, to remove the second parameter that tells `fetchOrderDetail` whether items have been retrieved or not, as shown:

>     RUN fetchOrderDetail IN hOrderProc
>             (INPUT iOrderNum,
>              /* Don't get all Items */
>              OUTPUT DATASET dsOrder APPEND).

> In this case, `fetchOrderDetail` will never return any `Items`. They will be retrieved one at a time as they’re needed.

> 3. Edit the `fetchOrderDetail` internal procedure in `OrderSupportBatch.p` to remove the second parameter. In addition, set the `FILL-MODE` for `ttItem` to be `NO-FILL`, unconditionally. This means that `fetchOrderDetail` will return only `OrderLines` for the current `Order`, and no `Items`. For example:

>     PROCEDURE fetchOrderDetail:
>          DEFINE INPUT  PARAMETER piOrderNum  AS INTEGER    NO-UNDO.
>          /* Removed lFillItems parameter -- never return all items */
>          DEFINE OUTPUT PARAMETER DATASET FOR dsOrder BY-VALUE.

>          hDataSet:EMPTY-DATASET.
>          cSelection = "OrderNum = " + STRING(piOrderNum).
>          hDataSet:GET-BUFFER-HANDLE(2):FILL-MODE = "APPEND".  /* ttOline */
>          hDataSet:GET-BUFFER-HANDLE(3):FILL-MODE = "NO-FILL". /* ttItem  */
>          hDataSet:FILL().

>      END PROCEDURE.  /* fetchOrderDetail */

> 4. Now code the `FindFailedItem` internal procedure in `PickOrderBatch.w`, as shown:

>     DEFINE INPUT PARAMETER DATASET FOR dsOrder.
>         DEFINE VARIABLE cItemName AS CHARACTER  NO-UNDO.
>         RUN fetchItem IN hOrderProc (ttOline.ItemNum, OUTPUT cItemName).
>         CREATE ttItem.
>         ASSIGN ttItem.ItemNum = ttOline.ItemNum
>                ttItem.ItemName = cItemName.
>         RETURN NO-APPLY.
>     END PROCEDURE.

> As with all callbacks, this one gets the ProDataSet as `INPUT`. It runs a new procedure called fetch`Item`, which you’ll write in a moment, which accepts the needed `Item` number and returns its `ItemName`. As always with callbacks, the buffer in the appropriate temp-table in the ProDataSet parameter holds the row that was current when the event occurred. In this case Progress is attempting to reposition the `ttItem` browse each time you select a `ttOline` row in its browse. To do this, Progress is doing the equivalent of a `FIND` statement internally. When the `Item` is not already there, the `FIND-FAILED` event occurs, giving your event handler the opportunity to add the needed `Item` to the client. Since the browse shows only the `ItemNum` and `ItemName` fields, `fetchItem` only needs to return the name. You then create a new `ttItem` temp-table row for that item, and `RETURN NO-APPLY`. This tells Progress to ignore the failure and try to locate the row again, transparent to wherever the find failed. This now succeeds, and the new `Item` appears in its browse and becomes the currently selected row.

> 5. Finally, write the new `fetchItem` procedure in `OrderSupportBatch.p`, as shown:

>     PROCEDURE fetchItem:
>          DEFINE INPUT  PARAMETER piItemNum  AS INTEGER   NO-UNDO.
>          DEFINE OUTPUT PARAMETER pcItemName AS CHARACTER NO-UNDO.
>          FIND Item WHERE Item.ItemNum = piItemNum.
>          pcItemName = Item.ItemName.
>      END.

> This accepts the current `ItemNum`, which the window procedure discovered that it didn’t have when it tried to reposition the `Item` browse when you select an `OrderLine`. It finds the database record for that `Item` and returns its `ItemName`. Since you’re just returning a single field, there’s no need to do a `FILL`. That would be overkill in this case.

> 6. Save all of this and rerun `PickOrderBatch.w`. Enter some selection criteria for `Order`s, such as `Customer 1`, tab through the fill-ins, and then double-click on an `Order` to retrieve its `OrderLines`.

> When you do this, your modified version of `fetchOrderDetail` is not returning any `Items`. Only when you select an `OrderLine` does Progress detect that its `Item` is missing, because there’s no row for it to reposition the `Item` browse to. This fires your `FIND-FAILED` event handler, which retrieves that one `Item`, and adds it to the temp-table. By executing a `RETURN NO-APPLY`, the handler tells Progress to ignore the failure and retry the reposition. Now it succeeds, as shown:

> As you continue to select `OrderLines`, Progress continues to retrieve each `Item` the first time it’s needed, adding it to the `ttItem` temp-table and to the browse, as shown:

> Note that because you’ve created a general-purpose event handler for any `FIND` on the `ttItem` table that fails, any other action your client might take that tries to find a `ttItem` row unsuccessfully will automatically trigger the same handler, and will transparently add the row to the temp-table and make the row available to the statement that needed it, without that statement or its surrounding code needing to do anything special to allow for the possibility that the row is not yet there when it is first referenced. This is the value of the event handlers, that they execute whenever they are needed within the session.

> Retrieving rows to cache on the client one at a time may not be a good strategy in many cases. Your `FIND-FAILED` handler could also retrieve batches of rows up to the one that is needed, or batches of related rows that are likely to be needed by the client. In any case, the `FIND-FAILED` event is just one more tool you can use to make batching and caching data in your ProDataSets as transparent as possible.

Copyright © 2005 Progress Software Corporation www.progress.com Voice: (781) 280-4000 Fax: (781) 280-4095