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 FUNCTIONNote:
- Line
1defines the function, taking no parameters and returning no values. - Line
3defines theopvariable 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 SQLUPDATEshould be performed.I: a new row has been created: An SQLINSERTmust be performed.
- Lines
7thru10define theINPUT ARRAYinstruction binding and options. - Lines
12thru28implement theBEFORE DELETEcontrol block. This block is used to ask the user to confirm the row deletion, and we perform the SQLDELETEstatement to remove the row from the database table. If the row deletion needs to be canceled, use theCANCEL DELETEinstruction inBEFORE DELETE. - Lines
30thry36implement theAFTER DELETEcontrol 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
38thru44define theAFTER FIELDcontrol block for thecust_namefield. 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 CURRENTinstruction, if needed. - Lines
46thru48: TheON ROW CHANGEcontrol 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. TheUPDATESQL statement will be performed inATFER ROW. - Lines
50thru55: TheBEFORE INSERTcontrol 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 theopstate"T"for "temporary". - Lines
59thru59: When moving to a another row after inserting a new row, theAFTER INSERTcontrol block is fired, to validate the new row creation. Here we set the op state to"I"for "inserted". TheINSERTSQL statement will be performed inATFER ROW. - Line
61thru63: TheBEFORE ROWcontrol block is executed after all other triggers, when the user goes to a different row in the record list. Here we can reset theopstate to"N", indicating that we are in "browsing mode". - Lines
65thru94: This is theAFTER ROWcontrol 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 FIELDin the context of theAFTER ROWcontrol block. Therefore, theINSERTandUPDATESQL statements are performed in theAFTER ROWblock, 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: