20. Januar 2007

One Time Timer

Sehr oft muss ich während der Validierung eine Navigation starten. Restricted Built-Ins kann man jedoch nicht benutzen. Wir brauchen einen Workaround:

Dies ist der "One Time Timer":

Beispiel: Ich habe einen Control-Block mit ein paar Feldern. Unter diesem Block ist ein Multi-Record Block namens EMP. Der Control-Block dient als Filter.

Der Anwender gibt Filterkriterien ein und automatisch wird nach jedem ausgefüllten Feld eine Query im darunterliegenden EMP Block gestartet. Das ist jedoch unmöglich, da Navigation im Validierungstrigger nicht erlaubt ist und in diesem Fall ein Go_Block und Execute_Query benötigt würden.

Lösung: Erstelle einen Form-Ebene WHEN-TIMER-EXPIRED-Trigger:

DECLARE
V_Item VARCHAR2 (61);
BEGIN
V_Item := :SYSTEM.CURSOR_ITEM;

IF One_Time_Timer.Get_Value = Const.ott_Query_in_EMP THEN
Go_Block ('EMP');
Execute_Query;
Go_Item (V_Item);
ELSIF One_Time_Timer.Get_Value = Const.ott_Something_Else
THEN
-- if more One-Time-Timer are needed,
-- create one for each Branch
NULL;
END IF;
END;

Erstelle eine Konstanten-Package mit einigen Konstanten:

PACKAGE Const IS

-- Globals
gbl_One_Time_Timer CONSTANT VARCHAR2 (61) :=
upper ('global.One_Time_Timer');

-- One-Time-Timer
ott_Query_in_EMP CONSTANT VARCHAR2 (30) :=
'Filter EMP-Block';
ott_Something_Else CONSTANT VARCHAR2 (30) :=
'Something else';

END;

und ein Package mit einigen Funktionen:

PACKAGE One_Time_Timer IS
FUNCTION Get_Value RETURN VARCHAR2;
PROCEDURE Initialize (P_Event IN VARCHAR2);
END;

PACKAGE BODY One_Time_Timer IS
FUNCTION Get_Value RETURN VARCHAR2 IS
BEGIN
Default_Value (NULL, Const.gbl_One_Time_Timer);
RETURN (NAME_IN (Const.gbl_One_Time_Timer));
END;

PROCEDURE Initialize (P_Event IN VARCHAR2) IS
tm_id timer;
tm_name VARCHAR2 (30) := 'ONE_TIME_TIMER';
BEGIN
tm_id := Find_Timer (tm_name);
IF ID_Null (tm_id) THEN
tm_id := Create_Timer (tm_name, 10, NO_REPEAT);
COPY (p_Event, Const.gbl_One_Time_Timer);
END IF;
END;
END One_Time_Timer;

Der Control-Block (namens "Filter") hat z.B. zwei Items: ENAME und SAL

Erstelle einen WHEN-VALIDATE-ITEM auf ENAME:

BEGIN
One_Time_Timer.Initialize (Const.ott_Query_in_EMP);
END;

letzter Schritt: der EMP-Block benötigt einen PRE-QUERY-Trigger auf Block-Ebene:

BEGIN
IF :Filter.ENAME IS NOT NULL THEN
:EMP.ENAME := :Filter.ENAME;
END IF;
END;


was passiert nun?
Nach dem Ändern eines Wertes in ENAME im FILTER-Block startet der WHEN-VALIDATE-ITEM auf diesem Feld. Er initialisiert den One-Time-Timer. Die globale Variable "global.One_Time_Timer" bekommt den Wert Const.ott_Query_in_EMP zugewiesen (= "Filter EMP-Block"). Danach startet der Timer und feuert nach 10 ms.

10ms später:
Der WHEN-TIMER-EXPIRED startet den execute_query im EMP-block und springt danach zurück auf das Feld, in dem der Cursor stand. Im EMP-block startet der PRE-QUERY und die Daten des EMP-Blocks werden gefiltert durch ":EMP.ENAME := :Filter.ENAME"

das war's schon. Viel Spass damit!

Keine Kommentare: