Back to Summary


Tutorial Chapter 8: Array Input

Summary:


This program uses a form and a screen array to allow the user to view and change multiple records of a program array at once.  The INPUT ARRAY statement and its control blocks are used by the program to control and monitor the changes made by the user to the records. As each record in the program array is Added, Updated, or Deleted,  the program logic makes corresponding changes in the rows of the customer database table.

The example window shown below has been re-sized to fit on this page.

                                             Display on Windows platform


The INPUT ARRAY statement

The INPUT ARRAY statement supports data entry by users into a screen array, and stores the entered data in a program array of records. During the INPUT ARRAY execution, the user can edit or delete existing records, insert new records, and move inside the list of records.  The program can then use the INSERT, DELETE or UPDATE SQL statements to modify the appropriate database tables. The INPUT ARRAY statement does not terminate until the user validates or cancels the dialog.

   INPUT ARRAY cust_arr WITHOUT DEFAULTS FROM sa_cust.* 
        ATTRIBUTE (UNBUFFERED, COUNT=idx)

The example INPUT ARRAY statement binds the screen array fields in sa_cust to the member records of the program array cust_arr. The number of variables in each record of the program array must be the same as the number of fields in each screen record (that is, in a single row of the screen array). Each mapped variable must have the same or a compatible data type as the corresponding field.

WITHOUT DEFAULTS clause

The WITHOUT DEFAULTS clause prevents BDL from displaying any default values that have been defined for form fields. You must use this clause if you want to see the values of the program array.

The UNBUFFERED attribute

As in the INPUT statement, when the UNBUFFERED attribute is used, the INPUT ARRAY statement is sensitive to program variable changes. If you need to display new data during the execution, use the UNBUFFERED attribute and assign the values to the program array row; the runtime system will automatically display the values to the screen.  This sensitivity applies to ON ACTION control blocks, as well.

COUNT and MAXCOUNT attributes

Some of the attributes of the INPUT ARRAY statement are:

Control Blocks

Control blocks in the INPUT ARRAY statement allow your program to control and monitor any changes the user makes. The control blocks that are used in the example program are:

See INPUT ARRAY for a complete list of control blocks.

Built-in Functions - ARR_CURR

The language provides several built-in functions to use in an INPUT ARRAY statement. The example program uses the ARR_CURR function to tell which array element is being changed.  This function returns the row number within the program array that is displayed in the current line of a screen array.

Predefined actions

There are some pre-defined actions that are specific to the INPUT ARRAY statement, to handle the insertion and deletion of rows in the screen array automatically:

As with the pre-defined actions accept and cancel actions discussed in Chapter 4, if your form specification does not contain action views for these actions, default action views (buttons on the form) are automatically created. Control attributes of the INPUT ARRAY statement allow you to prevent the creation of these actions and their accompanying buttons. 


Example: Using a Screen Array to modify Data

The Form Specification File

The custallform.per form specification file displays multiple records at once, and is similar to the form used in chapter 7.  The item type of field f6, containing the state values, has been changed to COMBOBOX to provide the user with a dropdown list when data is being entered.

Form file (custallform.per)
01 SCHEMA custdemo
02
03 LAYOUT
04  TABLE
05  {
06   Id   Name         ..  Zipcode   Contact          Phone
07  [f01][f02         ]   [f07     ][f08            ][f09         ]
08  [f01][f02         ]   [f07     ][f08            ][f09         ]
09  [f01][f02         ]   [f07     ][f08            ][f09         ]
10  [f01][f02         ]   [f07     ][f08            ][f09         ]
11  [f01][f02         ]   [f07     ][f08            ][f09         ]
12  [f01][f02         ]   [f07     ][f08            ][f09         ]
13  }
14  END
15 END
16
17 TABLES
18  customer
19 END
20
21 ATTRIBUTES
22 EDIT     f01 = customer.store_num, REQUIRED;
23 EDIT     f02 = customer.store_name, REQUIRED; 
24 EDIT     f03 = customer.addr;
25 EDIT     f04 = customer.addr2;
26 EDIT     f05 = customer.city;
27 COMBOBOX f6  = customer.state, ITEMS = ("IA", "IL", "WI");
28 EDIT     f07 = customer.zipcode;
29 EDIT     f08 = customer.contact_name;
30 EDIT     f09 = customer.phone;
31 END
32
33 INSTRUCTIONS
34 SCREEN RECORD sa_cust (customer.*);
35 END

The Main block

The single module program custall.4gl allows the user to update the customer table using a form that displays multiple records at once.

Main block  (custall.4gl)
01 SCHEMA custdemo
02
03 DEFINE cust_arr DYNAMIC ARRAY OF RECORD
04         store_num    LIKE customer.store_num,
05         store_name   LIKE customer.store_name,
06         addr         LIKE customer.addr,
07         addr2        LIKE customer.addr2,
08         city         LIKE customer.city,
09         state        LIKE customer.state,
10         zipcode      LIKE customer.zipcode,
11         contact_name LIKE customer.contact_name,
12         phone        LIKE customer.phone
13        END RECORD
14 
15
16 MAIN
17   DEFINE idx SMALLINT
18
19   DEFER INTERRUPT
20   CONNECT TO "custdemo"
21   CLOSE WINDOW SCREEN
22   OPEN WINDOW w3 WITH FORM "custallform"
23
24   CALL load_custall() RETURNING idx
25   IF idx > 1 THEN
26      CALL inparr_custall(idx)
27   END IF
28
29   CLOSE WINDOW w3
30   DISCONNECT CURRENT
31
32 END MAIN

Notes:


Function load_custall

This function loads the program array with rows from the customer database table. The logic to load the rows is identical to that in Chapter 7. Although this program loads all the rows from the customer table, the program could be written to allow the user to query first, for a subset of the rows.  A query-by-example, as illustrated in chapter 4, can also be implemented using a form containing a screen array such as manycust.

Function load_custall (custall.4gl)
01 FUNCTION load_custall()
02   DEFINE cust_rec RECORD LIKE customer.*,
03          idx SMALLINT
04
05   DECLARE custlist_curs CURSOR FOR
06      SELECT store_num, 
07          store_name, 
08          addr,
09          addr2,
10          city, 
11          state, 
12          zipcode, 
13          contact_name, 
14          phone
15        FROM customer
16        ORDER BY store_num
17
18   LET idx = 0
19   CALL cust_arr.clear()
20   FOREACH custlist_curs INTO cust_rec.*
21      LET idx = idx + 1
22      LET cust_arr[idx].* = cust_rec.*
23   END FOREACH
24    
25   IF (idx == 0) THEN 
26      MESSAGE "No rows loaded." 
27   END IF
28
29   RETURN idx
30
31 END FUNCTION

Notes:


Function inparr_custall

This is the primary function of the program, driving the logic for inserting, deleting, and changing rows in the customer database table.  Each time a record is added, deleted, or changed on the form, the values from the current record in the program array are used to update the customer table.  

Function inparr_custall (custall.4gl)
01 FUNCTION inparr_custall(idx)
02   DEFINE idx SMALLINT,
03         curr_pa SMALLINT,
04         opflag  CHAR(1)
05
06  INPUT ARRAY cust_arr WITHOUT DEFAULTS
07      FROM sa_cust.* 
08      ATTRIBUTE (UNBUFFERED, COUNT=idx)
09
10   BEFORE INPUT
11    MESSAGE "OK exits/" ||
12    "Cancel exits & cancels current operation"
13
14   BEFORE ROW
15     LET curr_pa = ARR_CURR()
16     LET opflag = "U"
17
18   BEFORE INSERT
19     LET opflag = "I"
20 
21   AFTER INSERT
22     CALL insert_cust(curr_pa)
23
24   BEFORE DELETE
25     IF NOT (delete_cust(curr_pa)) THEN
26       CANCEL DELETE
27     END IF
28
29   ON ROW CHANGE
30     CALL update_cust(curr_pa)
31
32   BEFORE FIELD store_num
33    IF (opflag <> "I") THEN
34      NEXT FIELD store_name
35    END IF
36
37   ON CHANGE store_num
38    IF (opflag = "I") THEN  
39       IF NOT store_num_ok(curr_pa) THEN
40        MESSAGE "Store already exists"
41        LET cust_arr[curr_pa].store_num = NULL
42        NEXT FIELD store_num
43      END IF 
44    END IF
45
46  END INPUT
47
48  IF (INT_FLAG) THEN
49    LET INT_FLAG = FALSE
50  END IF
51
52 END FUNCTION -- inparr_custall

Notes:


Function store_num_ok

When a new record is being inserted into the program array, this function verifies that the store number does not already exist in the customer database table. The logic in this function is virtually identical to that used in Chapter 5.

Function store_num_ok (custall.4gl)
01 FUNCTION store_num_ok(idx)
02   DEFINE idx SMALLINT,
03         checknum LIKE customer.store_num,
04         cont_ok SMALLINT
05
06   LET cont_ok = FALSE
07   WHENEVER ERROR CONTINUE 
08   SELECT store_num INTO checknum
09     FROM customer 
10     WHERE store_num = 
11          cust_arr[idx].store_num
12   WHENEVER ERROR STOP     
13   IF (SQLCA.SQLCODE = NOTFOUND) THEN
14     LET cont_ok = TRUE 
15   ELSE
16     LET cont_ok = FALSE
17     IF (SQLCA.SQLCODE = 0) THEN 
18       MESSAGE "Store Number already exists."
19     ELSE
20       ERROR SQLERRMESSAGE
21     END IF
22   END IF
23 
24   RETURN cont_ok
25
26 END FUNCTION

Notes:


Function insert_cust

This function inserts a new row into the customer database table.

Function insert_cust (custall.4gl)
01 FUNCTION insert_cust(idx)
02   DEFINE idx SMALLINT
03
04   WHENEVER ERROR CONTINUE
05   INSERT INTO customer 
06       (store_num, 
07        store_name, 
08        addr, 
09        addr2, 
10        city, 
11        state, 
12        zipcode, 
13        contact_name, 
14        phone)
15     VALUES (cust_arr[idx].* )
16   WHENEVER ERROR STOP
17
18   IF (SQLCA.SQLCODE = 0) THEN
19     MESSAGE "Store added"
20   ELSE
21     ERROR SQLERRMESSAGE
22   END IF
23
24 END FUNCTION

Notes:


Function update_cust

This function updates a  row in the customer database table.  The functionality is very simple for illustration purposes, but it could be enhanced with additional error checking routines similar to the example in chapter 6.

Function update_cust (custall.4gl)
01 FUNCTION update_cust(idx)
02   DEFINE idx SMALLINT
03   
04   WHENEVER ERROR CONTINUE
05   UPDATE customer 
06    SET 
07     store_name   = cust_arr[idx].store_name,
08     addr         = cust_arr[idx].addr,
09     addr2        = cust_arr[idx].addr2, 
10     city         = cust_arr[idx].city, 
11     state        = cust_arr[idx].state, 
12     zipcode      = cust_arr[idx].zipcode, 
13     contact_name = cust_arr[idx].contact_name,
14     phone        = cust_arr[idx].phone 
15    WHERE store_num = cust_arr[idx].store_num
16   WHENEVER ERROR STOP
17
18   IF (SQLCA.SQLCODE = 0) THEN
19      MESSAGE "Dealer updated."
20   ELSE
21      ERROR SQLERRMESSAGE
22   END IF
23
24 END FUNCTION

Notes:


Function delete_cust

This function deletes a row from the customer database table.  A modal Menu similar to that illustrated in Chapter 6 is used to verify that the user wants to delete the row.

Function delete_cust (custall.4gl)
01 FUNCTION delete_cust(idx)
02   DEFINE idx     SMALLINT,
03         del_ok      SMALLINT
04
05   LET del_ok = FALSE
06
07   MENU "Delete" ATTRIBUTE (STYLE="dialog", 
08               COMMENT="Delete this row?")
09    COMMAND "OK"
10      LET del_ok = TRUE
11      EXIT MENU
12    COMMAND "Cancel"
13      LET del_ok = FALSE 
14      EXIT MENU
15   END MENU
16
17   IF del_ok = TRUE THEN 
18     WHENEVER ERROR CONTINUE
20     DELETE FROM customer 
21        WHERE store_num = cust_arr[idx].store_num
22     WHENEVER ERROR STOP
23
24     IF (SQLCA.SQLCODE = 0) THEN
25       LET del_ok = TRUE
26       MESSAGE "Dealer deleted."
27     ELSE
28       LET del_ok = FALSE
29       ERROR SQLERRMESSAGE
30     END IF
31   END IF
32
33   RETURN del_ok
34
35 END FUNCTION

Notes: