Example: custlistinp.4gl (Function custarr_input)
The custlistinp.4gl module implements the INPUT
ARRAY
dialog in the custarr_input
function.
The
custarr_input
function in
custlistinp.4gl: 1 PRIVATE FUNCTION custarr_input() RETURNS ()
2 DEFINE x INTEGER
3 DEFINE op CHAR(1)
4
5 LET int_flag = FALSE
6
7 INPUT ARRAY custarr FROM sa_cust.*
8 ATTRIBUTES(UNBUFFERED,
9 CANCEL = FALSE,
10 WITHOUT DEFAULTS)
11
12 BEFORE DELETE
13 DISPLAY "BEFORE DELETE: op=",op
14 IF op == "N" THEN
15 LET x = arr_curr()
16 IF NOT comutils.mbox_yn("Customers",
17 "Are you sure you want to delete this record?")
18 THEN
19 CANCEL DELETE
20 END IF
21 TRY
22 DELETE FROM customer
23 WHERE cust_num = custarr[x].cust_num
24 CATCH
25 ERROR SQLERRMESSAGE
26 CANCEL DELETE
27 END TRY
28 END IF
29
30 AFTER DELETE
31 DISPLAY "AFTER DELETE: op=",op
32 IF op == "N" THEN
33 MESSAGE "Record has been deleted successfully"
34 ELSE
35 LET op = "N"
36 END IF
37
38 AFTER FIELD cust_name
39 DISPLAY "AFTER FIELD cust_name: op=",op
40 LET x = arr_curr()
41 IF custarr[x].cust_name MATCHES "*@#$%^&()*" THEN
42 ERROR "This field contains invalid characters"
43 NEXT FIELD CURRENT
44 END IF
45
46 ON ROW CHANGE
47 DISPLAY "ON ROW CHANGE: op=",op
48 IF op != "I" THEN LET op = "M" END IF
49
50 BEFORE INSERT
51 DISPLAY "BEFORE INSERT: op=",op
52 LET op = "T"
53 LET x = arr_curr()
54 LET custarr[x].cust_num = 1000 + arr_curr()
55 LET custarr[x].cust_name = "<undefined>"
56
57 AFTER INSERT
58 DISPLAY "AFTER INSERT: op=",op
59 LET op = "I"
60
61 BEFORE ROW
62 DISPLAY "BEFORE ROW: op=",op
63 LET op = "N"
64
65 AFTER ROW
66 DISPLAY "AFTER ROW: op=",op
67 IF int_flag THEN EXIT INPUT END IF
68 LET x = arr_curr()
69 IF op == "M" OR op == "I" THEN
70 IF custarr[x].cust_name MATCHES "[0-9]*" THEN
71 ERROR "Customer name seems invalid"
72 NEXT FIELD cust_name
73 END IF
74 END IF
75 IF op == "I" THEN
76 TRY
77 INSERT INTO customer VALUES ( custarr[x].* )
78 MESSAGE "Record has been inserted successfully"
79 CATCH
80 ERROR SQLERRMESSAGE
81 NEXT FIELD CURRENT
82 END TRY
83 END IF
84 IF op == "M" THEN
85 TRY
86 LET x = arr_curr()
87 UPDATE customer SET customer.* = custarr[x].*
88 WHERE cust_num = custarr[x].cust_num
89 MESSAGE "Record has been updated successfully"
90 CATCH
91 ERROR "Could not update the record in database!"
92 NEXT FIELD CURRENT
93 END TRY
94 END IF
95
96 END INPUT
97
98 END FUNCTION
Note:
- Line
1
defines the function, taking no parameters and returning no values. - Line
3
defines theop
variable asCHAR(1)
, to hold the current state of theINPUT ARRAY
(the current operation):N
: in default, browsing state, current row already exists and is untouched.T
: temporary state, after starting the creation of a new row (insert or append action)M
: the current row has been modified: An SQLUPDATE
should be performed.I
: a new row has been created: An SQLINSERT
must be performed.
- Lines
7
thru10
define theINPUT ARRAY
instruction binding and options. - Lines
12
thru28
implement theBEFORE DELETE
control block. This block is used to ask the user to confirm the row deletion, and we perform the SQLDELETE
statement to remove the row from the database table. If the row deletion needs to be canceled, use theCANCEL DELETE
instruction inBEFORE DELETE
. - Lines
30
thry36
implement theAFTER DELETE
control block. This trigger is executed after the row was removed from the record list. This trigger may also be fired, if a new row was inserted, and the user decides to delete the new temporary row. In this case, the op variable is set to"T"
and we don't want to show a deletion confirmation message to the user. - Lines
38
thru44
define theAFTER FIELD
control block for thecust_name
field. Here we make sure that the customer name does not contain unexpected characters, and force the focus to stay in the field with aNEXT FIELD CURRENT
instruction, if needed. - Lines
46
thru48
: TheON ROW CHANGE
control block is used to detect if some fields were modified, before leaving the current row. We must check the value of the op state variable, and only set to"M"
(for modified row), if it has not a new inserted row. TheUPDATE
SQL statement will be performed inATFER ROW
. - Lines
50
thru55
: TheBEFORE INSERT
control block is used to detect the creation of a new row. In this block you typically set default values for the new row. Starting from here, the user can still delete the new created row, that's why we use theop
state"T"
for "temporary". - Lines
59
thru59
: When moving to a another row after inserting a new row, theAFTER INSERT
control block is fired, to validate the new row creation. Here we set the op state to"I"
for "inserted". TheINSERT
SQL statement will be performed inATFER ROW
. - Line
61
thru63
: TheBEFORE ROW
control block is executed after all other triggers, when the user goes to a different row in the record list. Here we can reset theop
state to"N"
, indicating that we are in "browsing mode". - Lines
65
thru94
: This is theAFTER ROW
control block where the code to update the database table must be implemented. WithINPUT ARRAY
, the only way to force the focus to stay in a row is to issue aNEXT FIELD
in the context of theAFTER ROW
control block. Therefore, theINSERT
andUPDATE
SQL statements are performed in theAFTER ROW
block, and in case of SQL error, we force the focus to stay in the invalid row.
To better understand the INPUT ARRAY
control block execution sequence, add
DISPLAY statements below each of the block markers, with the name of the block, and display the
value of the "op"
state variable: