Ask Reuben

BEFORE DISPLAY EXIT DISPLAY

Why are only X rows of my array rendered?

I briefly covered this topic as part of an article Tables 101 that covered a lot of topics to do with the use of TABLE container in forms.  I want to cover in a little more detail the following technique …

DISPLAY ARRAY arr TO scr.*
   BEFORE DISPLAY
      EXIT DISPLAY
END DISPLAY

… the key being to exit from the DISPLAY ARRAY as soon as it has been displayed.

This technique is useful when transforming old character screens that used the QAUD pattern to display data from two or more tables that have a Master-Detail relationship in the one screen form.  The old character screen might have had approximately 25 lines (80×25), the top portion would contain details about the current row from the master table whilst the bottom portion would contain data from multiple rows from the detail table.  As the user navigated through the rows from the master table, the top portion would show the values from the current row of the master table whilst the bottom portion would change to show multiple rows from the detail table that are related to the current master row.  If the user wanted to scroll through the rows in the detail table, a seperate dialog was required.

As the screens were character based, the number of rows from the detail table displayed in the bottom portion of the form were fixed.  For the purposes of this article I will say that the number of detail rows able to be displayed is 5.  So the existing code would be similar to …

   MENU ""
      ...
      COMMAND "Next" -- Previous would be similar
      ...
         CALL fetch_detail_rows()
         CALL show_detail_rows()
      ...

FUNCTION show_detail_rows()
...
   FOR i = 1 TO 5
      DISPLAY detail_arr[i] TO detail_scr[i].*
   END FOR
...

When transforming to Genero, the bottom portion of the screen was typically changed to a TABLE, but what was not taken into account was that the Table could stretch and that there would be room for more than 5 rows of data.

Using the example code at the end of the article, your initial screen might look like the following with 5 rows of data displayed …

… If you next and previous through the 9 rows of the master you see the value 1 through 9 in the top portion of the screen, and the detail rows in the bottom portion of the screen is the master id followed by A-Z e.g. 1A, there are 26 detail rows for each master record.   Note what happens when the user stretches the window.  More rows are not displayed to this bottom portion and so the user gets the false impression that there are only 5 rows of data in the detail table…

The quick solution at this point is to use the  WANTFIXEDPAGESIZE to tell the Table not to stretch.  This will prevent the user getting the false impression that there is only 5 rows of data but you will end up with unused space…

What the BEFORE DISPLAY EXIT DISPLAY technique does is instead of a FOR loop displaying a fixed number of lines from the detail table, replace the FOR loop with a DISPLAY ARRAY statement that consists of a BEFORE DISPLAY EXIT DISPLAY.  That is your code is now of the form …

MENU "" 
   ...   
   COMMAND "Next" -- Previous would be similar   
      ... 
      CALL fetch_detail_rows()     
      CALL show_detail_rows()
      ... 

FUNCTION show_detail_rows() 
   ... 
   DISPLAY ARRAY detail_arr[master_idx] TO detail_scr[master_idx].*
      BEFORE DISPLAY
         EXIT DISPLAY
   END DISPLAY
   ...

Now instead of the code being hard coded to display a fixed number of rows from the detail row, the DISPLAY ARRAY will display as many rows as can fit in the table, and will immediately exit passing the user back to the previous dialog …

There is one further enhancement that can be made if you go down this path.  If the user resizes the window, you might want to display additional rows.  So your MENU statement might now have an ON ACTION windowresized added to the dialog used to display rows in the master table.

MENU "" 
   ...   
   COMMAND "Next" -- Previous would be similar   
      ... 
      CALL fetch_detail_rows()     
      CALL show_detail_rows()
      ... 
   ON ACTION windowresized
      CALL show_detail_rows()

FUNCTION show_detail_rows() 
   ... 
   DISPLAY ARRAY detail_arr[master_idx] TO detail_scr[master_idx].*
      BEFORE DISPLAY
         EXIT DISPLAY
   END DISPLAY
   ...

So note the window has been stretched and the additional detail rows are visible…

It should be stressed that this technique of using DISPLAY ARRAY BEFORE DISPLAY EXIT DISPLAY helps when you don’t want to transform the code too much.  It allows you to use the GUI Table container to display the detail rows in a form that is displaying both master and detail, and does so in a way that does not involve major surgery to your 4gl code..

Ideally to display a master-detail in a GUI environment you would use Multiple Dialog with two active DISPLAY ARRAY statements, that approach requires more surgery on your code.  That might be the topic of a future Ask-Reuben article, the key being to use UNBUFFERED and to repopulate the detail array in the BEFORE ROW of the master array.

#! askreuben193.4gl
TYPE arrType RECORD
    code CHAR(2)
END RECORD
DEFINE arr1, arr2, arr3 DYNAMIC ARRAY OF arrType
DEFINE idx INTEGER

MAIN
    OPEN WINDOW w WITH FORM "askreuben193"
    CLOSE WINDOW SCREEN

    MENU ""
        ON ACTION query ATTRIBUTES(TEXT="Query")
            LET idx = 1
            CALL populate(idx)
            CALL display(idx)

        ON ACTION previous  ATTRIBUTES(TEXT="Previous")
            LET idx = idx - 1
            IF idx < 1 THEN
                LET idx = 1
            END IF
            CALL populate(idx)
            CALL display(idx)

        ON ACTION next  ATTRIBUTES(TEXT="Next")
            LET idx = idx + 1
            IF idx > 9 THEN
                LET idx = 9
            END IF
            CALL populate(idx)
            CALL display(idx)

        ON ACTION windowresized  ATTRIBUTES(DEFAULTVIEW=NO)
            CALL display(idx)

        ON ACTION close
            EXIT MENU
    END MENU
END MAIN

FUNCTION populate(idx INTEGER)
    DEFINE code CHAR(2)
    DEFINE letter INTEGER

    -- Populate
    FOR letter = 1 TO 26
        LET code = SFMT("%1%2", idx USING "&", ASCII (64 + letter))
        LET arr1[letter].code = code
        LET arr2[letter].code = code
        LET arr3[letter].code = code
    END FOR
END FUNCTION

FUNCTION display(idx INTEGER)
    DEFINE row INTEGER

    -- Display current Index Value
    DISPLAY idx TO idx
    MESSAGE SFMT("Row %1 of %2", idx, 9)

    --  Old 4gl technique
    FOR row = 1 TO 5
        DISPLAY arr1[row].code TO scr1[row].col1
    END FOR

    -- Using WANTFIXEDPAGESIZE, can use old technique
    FOR row = 1 TO 5
        DISPLAY arr2[row].code TO scr2[row].col2
    END FOR

    -- Use BEFORE DISPLAY, EXIT DISPLAY
    DISPLAY ARRAY arr3 TO scr3.*
        BEFORE DISPLAY
            EXIT DISPLAY
    END DISPLAY
END FUNCTION
#!askreuben193.per
LAYOUT  (TEXT="Ask Reuben 193")
VBOX
GROUP(TEXT="Master Table")
GRID
{
Master ID [i00]
Field 1   [i01        ]
Field 2   [i02        ]
}
END
END
GROUP (TEXT="Detail Table")
HBOX
GROUP (TEXT="Default Technique")
TABLE
{
[t01                ]
[t01                ]
[t01                ]
[t01                ]
[t01                ]
}
END
END
GROUP (TEXT="With WantFixedPageSize")#, HIDDEN)
VBOX
TABLE (WANTFIXEDPAGESIZE)
{
[t02                ]
[t02                ]
[t02                ]
[t02                ]
[t02                ]
}
END
GRID
{
[spring01]
}
END
END
END
GROUP (TEXT="Using Before Display Exit Display")#, HIDDEN)
TABLE 
{
[t03                ]
}
END
END
END
END
END
END
ATTRIBUTES
EDIT i00 = formonly.idx;
EDIT i01 = formonly.master1;
EDIT i02 = formonly.master2;

EDIT t01 = formonly.col1, TITLE="Detail Column 1";
EDIT t02 = formonly.col2, TITLE="Detail Column 1";
EDIT t03 = formonly.col3, TITLE="Detail Column 1";

IMAGE spring01 = formonly.spring01, STRETCH=BOTH;


INSTRUCTIONS
SCREEN RECORD scr1(col1);
SCREEN RECORD scr2(col2);
SCREEN RECORD scr3(col3);