In an updateable browser, you might want to allow the user to add new records to the underlying temp-table or database table. Programmatically, this requires three separate steps:

  1. Create a blank line in the browse viewport with the INSERT-ROW() method and populate it with new data. INSERT-ROW takes a single optional argument, which is the string “BEFORE” or “AFTER”. This argument tells the AVM whether to insert the new row before or after the currently selected row in the browse. The default is “BEFORE”. You can use the INSERT-ROW() browse method in an empty browse. It places a new row at the top of the viewport.
  2. Use the CREATE statement and ASSIGN statement to update the database or the underlying temp-table.
  3. Add a reference to the result list with the CREATE-RESULT-LIST-ENTRY() method. This is the list of record identifiers that the AVM uses to keep track of the set of rows in the query. This step is necessary only if you do not plan to reopen the query after the update, because reopening the query completely refreshes the list. However, this method makes reopening the query unnecessary for most applications.

All three steps are required to create the record and keep the database, query, and browse in sync. Also, there are several possible side effects to allowing the user to add a record through an updateable browse. They include placing new records out of order and adding records that do not match the query. To eliminate these side effects, you can reopen the query after each new record is added.

To add a button to the test window that lets you add new OrderLines to the temp-table through the OlineBrowse:
  1. In the Definitions section of the h-CustOrderWin5.w procedure, beneath the browse defined for OlineBrowse, define a handle variable called hBrowse. You use this variable to hold the handle of the OrderLine browse because it is needed in several places.
  2. Define a new buffer for the OrderLine temp-table called ttOline2. You use this buffer to hold onto the values in the current temp-table record as you create a new record, and to copy some of the values from the old record to the new one.
  3. Assign the OlineBrowse handle to the new hBrowse variable:
    DEFINE VARIABLE hBrowse AS HANDLE NO-UNDO.
    DEFINE BUFFER ttOline2 FOR ttOline.
    
    hBrowse = BROWSE OlineBrowse:HANDLE.
  4. Drop a new button onto the window where it will not be overwritten by the OrderLine browse when it is displayed.
  5. Name the new button btnNew and give it a label of New OrderLine.
  6. Define a CHOOSE trigger for the button:
    DO:
        IF hBrowse:NUM-SELECTED-ROWS = 0 THEN
        DO:
          APPLY "END" TO hBrowse.
          hBrowse:SELECT-FOCUSED-ROW().
        END.
        hBrowse:INSERT-ROW("AFTER").
    END.

    This trigger code uses the handle of the OlineBrowse that you just assigned up in the Definitions section and inserts a new row in the browse after the currently selected row. First, it checks to make sure that there is a selected row using the browse’s NUM-SELECTED-ROWS attribute. If the value of this attribute is zero, then the user has not selected any row. In this case, the trigger positions to the end of the browse by applying the END event to it and then selects that row using the SELECT-FOCUSED-ROW method.

  7. Back in the procedure’s Definitions section, add this ROW-LEAVE trigger block for the OlineBrowse following the statement that assigns its handle to hBrowse:
    ON "ROW-LEAVE" OF BROWSE OlineBrowse
    DO:
      IF hBrowse:NEW-ROW THEN
        DO:
          FIND LAST ttOline2.
          CREATE ttOline.
          BUFFER-COPY ttOline2 TO ttOline
            ASSIGN ttOline.LineNum = ttOline2.LineNum + 1.
          ASSIGN INPUT BROWSE OlineBrowse ttOline.Qty
            ttOline.Price ttOline.Discount.
          DISPLAY ttOline.OrderNum ttOline.LineNum
            ttOline.ItemNum ttOline.ItemName
            WITH BROWSE OlineBrowse.
          hBrowse:CREATE-RESULT-LIST-ENTRY().
        END.
    END.

    This code first checks the NEW-ROW attribute of the browse to see if the row the user left is one newly created by the New OrderLine button.

    Because you are not prepared to do a full-fledged database update from the browse yet, the example simply creates a new row in the temp-table. To simplify the example, you start by copying the fields from the previous record into the new one. To do this, you FIND the LAST record in the temp-table using the second buffer ttOline2. This gives you the highest LineNum value so that you can increment it for the new record. Note that this means that other initial field values are also assigned from the last record regardless of where in the viewport the user inserted the new record.

    You create a new temp-table record and BUFFER-COPY the last record into the new one, incrementing the LineNum field to give it a distinct value. This is important because the temp-table has the same unique index on the OrderNum and LineNum fields as the underlying OrderLine table in the database, so changing the LineNum has to happen in the BUFFER-COPY statement to avoid a unique index violation.

    Next, the code assigns values to those columns that are enabled for input. The INPUT keyword on the ASSIGN statement tells the AVM to take the values from the screen buffer for each column. Then the new values are displayed in the browse.

    Finally, you create a result list entry for the new record. If you did not create a result list entry, the row would be lost if you were to scroll it out of the browse’s viewport. Reopening the query would also rebuild the result list, and also assure that all rows are in the proper order, so you can use either technique for getting the browse and the query back in sync.

  8. Run the window. Now you can add a record to the OrderLine browse:

Once again, you learn how to get new records into the database in Update Your Database and Write Triggers.