Set up an event handler for the FIND-FAILED buffer event
- Last Updated: March 30, 2020
- 4 minute read
- OpenEdge
- Version 12.2
- Documentation
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 was not 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 FIND-FAILED event works:
-
Add a callback for a
FIND-FAILEDevent handler to theLEAVEtrigger forSalesRepin PickOrderBatch.w, right after the callback for theOFF-ENDevent. 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-ENDevent must be attached to a query, and theFIND-FAILEDevent to a ProDataSet buffer, in this case thettItembuffer, which will allow the procedure to retrieveItemsto add to the browse the first time they are referenced. -
Change the call to
fetchOrderDetailin theMOUSE-SELECT-DBLCLICKtrigger for theOrderbrowse by removing the second parameter that tellsfetchOrderDetailwhether items have been retrieved or not, as shown:/* Don't get all Items */ RUN fetchOrderDetail IN hOrderProc (iOrderNum, OUTPUT DATASET dsOrder APPEND).In this case,
fetchOrderDetailwill never return anyItems. They will be retrieved one at a time as they are needed. -
Edit the
fetchOrderDetailinternal procedure in OrderSupportBatch.p to remove the second parameter. In addition, set theFILL-MODEforttItemto beNO-FILL, unconditionally. This means thatfetchOrderDetailwill return onlyOrderLinesfor the currentOrder, and noItems. 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 */ -
Now code the
FindFailedIteminternal procedure in PickOrderBatch.w, as shown:PROCEDURE FindFailedItem: 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 fetchItem, which you will write in a moment, which accepts the neededItemnumber and returns itsItemName. 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 the AVM is attempting to reposition thettItembrowse each time you select attOlinerow in its browse. To do this, the AVM is doing the equivalent of aFINDstatement internally. When theItemis not already there, theFIND-FAILEDevent occurs, giving your event handler the opportunity to add the neededItemto the client.Since the browse shows only the
ItemNumandItemNamefields,fetchItemonly needs to return the name. You then create a newttItemtemp-table row for that item, andRETURN NO-APPLY. This tells the AVM to ignore the failure and try to locate the row again, transparent to wherever the find failed. This now succeeds, and the newItemappears in its browse and becomes the currently selected row. -
Finally, write the new
fetchItemprocedure 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 did not have when it tried to reposition theItembrowse when you select anOrderLine. It finds the database record for thatItemand returns itsItemName. Since you are just returning a single field, there is no need to do aFILL. That would be overkill in this case. -
Save all of this and rerun PickOrderBatch.w.
Enter some selection criteria for
Orders, such asCustomer 1, tab through the fill-ins, and then double-click on anOrderto retrieve itsOrderLines.When you do this, your modified version of
fetchOrderDetailis not returning anyItems. Only when you select anOrderLinedoes the AVM detect that itsItemis missing, because there is no row to which it can reposition theItembrowse. This fires yourFIND-FAILEDevent handler, which retrieves that oneItem, and adds it to the temp-table. By executing aRETURN NO-APPLY, the handler tells the AVM to ignore the failure and retry the reposition.Now theItemis retrieved as expected, as shown:
As you continue to selectOrderLines, the AVM continues to retrieve eachItemthe first time it is needed, adding it to thettItemtemp-table and to the browse, as shown:
Note that because you have created a general-purpose event handler for any
FINDon thettItemtable that fails, any other action your client might take that tries to find attItemrow 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-FAILEDhandler 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, theFIND-FAILEDevent is just one more tool you can use to make batching and caching data in your ProDataSets as transparent as possible.