Skip to main contentSkip to search
Powered by Zoomin Software. For more details please contactZoomin
Progress DocumentationProgress Documentation
Progress Documentation
  • Home
  • Home
  • EnglishČeštinaDeutsch (Germany)Español (Spain)Français (France)Italiano (Italy)Português (Brasil)日本語Русский (Russia)中文 (简体) (China)中文 (繁體, 台灣) (Taiwan)ar-AR
  • Login

Develop ABL Applications

Create nested blocks to display related data

Save PDF
Save selected topicSave selected topic and subtopicsSave all topics
Share
Share to emailCopy topic URL
Print
Table of Contents
  • Introduction to ABL
    • About the sample database
    • In the beginning . . . FOR EACH CUSTOMER
      • Start your OpenEdge session
      • Write your first procedure
    • ABL structure
    • An ABL procedure consists of statements
    • ABL combines procedural, database, and user interface statements
    • Save your test procedure
  • Use Basic ABL Constructs
    • Comparison operators
    • Use quotation marks
    • Create nested blocks to display related data
    • Change labels and formats
    • Use program variables and data types
      • Learn about the VAR statement
      • Define formats
      • Other variable qualifiers
      • Variable naming conventions
      • Placement of variable definitions
    • Define an IF-THEN-ELSE decision point
    • Use the ABL Unknown value
    • Use built-In ABL functions
    • ABL functions
      • ABL string manipulation functions
    • Put a calculation into your procedure
    • Get to online help
    • Save and compile your test procedure
  • Run ABL Procedures
    • Use the Propath
    • Use external and internal procedures
      • Write internal procedures
      • Assign a value to a variable
      • RETURN statement and RETURN-VALUE
    • Add comments to your procedure
  • Introduction to the OpenEdge AppBuilder
    • Start the AppBuilder
    • Create a new procedure and window
      • Add fields to your window
      • Change object names and titles
      • Save a procedure from the AppBuilder
      • Run your procedure
    • Use the Query Builder
      • Add a browse to your window
      • Use property sheets
    • Use the Section Editor
      • Look at preprocessor values in the Code Preview
      • Position within the query
    • Add buttons to your window
      • Define a CHOOSE trigger for your button
      • Define user interface events
      • Adjust the layout of the buttons
  • Examine the Code the AppBuilder Generates
    • View the entire sample procedure code
      • The Definitions section
      • Window, button, browse, and frame definitions
        • Define a handle variable for the window
        • Define a button
        • Define a query
        • Define a browse
        • Define a frame
      • Create the window
        • Static objects
        • Dynamic objects
        • Set attributes of dynamic objects
      • Define triggers
        • The button triggers
      • Triggers as event-driven code
      • Look at the main block
      • The internal procedures
    • Contrast procedural and event-driven programs
      • Advantages of the AppBuilder file format
    • Looking ahead
  • Procedure Blocks and Data Access
    • Procedure block scope
    • DO blocks
      • Loop with a DO block
      • Use a DO block to scope records and frames
    • FOR blocks
      • Join tables using multiple FOR phrases
      • Alternatives to the EACH keyword
      • Use indexes to relate and sort data
      • Use the USE-INDEX phrase to force a retrieval order
      • Use the LEAVE statement to leave a block
      • Use block headers to identify blocks
      • Use NEXT, STOP, and QUIT to change block behavior
      • Qualify a FOR statement with a frame reference
    • REPEAT blocks
      • Use the PRESELECT keyword to get data in advance
    • Data access without looping: the FIND statement
      • Index cursors
      • Use the FIND statement in a REPEAT block
      • Switch indexes between FIND statements
      • Use a USE-INDEX phrase to force index selection
      • Do a unique FIND to retrieve a single record
      • Use the CAN-FIND function
  • Record Buffers and Record Scope
    • Record buffers
    • Record scope
      • Self-contained references to a buffer
      • Generate a procedure listing file
      • Nested, weak-scoped references to the same buffer
      • Weak-scoped blocks and free references
      • Scope buffers
      • Strong-scoped references and containing blocks
    • Record buffer and error handling
    • Add procedures to the test window
      • Define h-OrderCalcs.p to calculate totals
        • Check the syntax of a procedure
        • Add fill-ins to the window for the calculations
        • Change field properties in the property sheet
        • Write a VALUE-CHANGED trigger for the browse
        • Add APPLY statements to the procedure
      • Write the h-BinCheck procedure to check inventory
        • Use list and string functions to manage a list of values
        • Use multiple weak-scoped references in a single block
        • Examine the scope with weak and strong references
      • Display the new fields in the window
  • Define Graphical Objects
    • Types of objects
    • Define static objects
      • Use the VIEW-AS phrase for data representation
      • Define objects that do not represent data values
    • Use and set object attributes
      • Change attribute values
      • Common attribute values for visual objects
        • Geometry attributes
        • Appearance attributes
        • Data management attributes
        • Relationship attributes
        • Identify attributes
    • Invoke object methods
    • Instantiate and realize objects
      • Instantiate objects in a container
      • Realize and derealize objects
    • Use object events
      • User interface events
      • Define triggers
        • Set up triggers to be executed
        • Make a trigger persistent
        • Use the REVERT statement to cancel a trigger
        • Define triggers to fire anywhere
      • Apply events in your application
        • Use NO-APPLY to suppress default processing for an event
        • Apply a nonstandard event
  • Use Graphical Objects in Your Interface
    • Use data representation objects
      • Modify fill-in attributes in the Properties Window
      • Change a fill-in field to an editor
        • Use the Properties Window to change a display type
        • Use the Section Editor’s pop-up menu to insert a value
        • Look at the code that defines the editor
      • Add a toggle box to the window
      • Define a slider
      • Objects that display a list of choices for a data value
        • Use a radio set to display a set of choices for a value
    • Use other types of objects
      • Use rectangles to organize objects
      • Use images to display pictures
      • Add text to the window
    • Specify drag and drop
    • Use menus
      • Menu bars
      • Define a menu bar
      • Assign a menu bar to a window
      • Define a menu bar for the sample window
        • Use the MENU-DROP event
        • Use OWNER, PARENT, FIRST-CHILD, and NEXT-SIBLING
      • Define a pop-up menu
      • Character-mode considerations
  • Use Queries
    • Queries versus block-oriented data access
    • Query uses
    • Define and use queries
      • OPEN and CLOSE QUERY statements
        • Use an outer join in a query
        • Sort the query results
      • GET statements
        • Use the QUERY-OFF-END function
      • Close a query
      • Determine the current number of rows in a query
        • Use a DOWN frame and the DOWN WITH statement
        • Retrieve query results in advance
      • Identify the current row in the query
        • Use INDEXED-REPOSITION
      • Reposition a query
        • Use a RowID to identify a record
        • Position details with the REPOSITION statement
    • Extend the sample window to use the queries
      • Use NUM-RESULTS to check the validity of the query
      • Use the MESSAGE statement
    • Example: Populate a browse using a freeform query
    • Summary
  • Define and Use Temp-tables
    • Use temp-tables in your application
      • Define a temp-table
        • Define fields for the temp-table
        • Define indexes for the temp-table
        • Temp-table scope
        • Temp-table buffers
    • Use a temp-table to summarize data
    • Use a temp-table as a parameter
      • Temp-table parameter syntax
      • Pass a temp-table by value
      • Pass a temp-table by reference
      • Pass a temp-table parameter by binding
      • Define a procedure to return Order Lines
      • Use BUFFER-COPY to assign multiple fields
    • Use include files to duplicate code
    • Add an Order Line browse to the Customer window
  • Use the Browse Object
    • Define a query for a browse
    • Define a browse
      • Change the test window for the OrderLine browse
      • Enable columns in the browse
      • Define a single- or multiple-select browse
      • Browse selection and query interaction
      • Use calculated columns
      • Size a browse and browse columns
      • Specify a widget type for displaying column data
    • Program with the browse
      • Browse events
        • Basic events
        • Row events
        • Column events
        • Search columns
      • Manipulate rows in the browse
        • Refresh browse rows
        • Reposition focus
        • Update browse rows
        • Create browse rows
        • Delete browse rows
      • Manipulate the browse itself
        • Set the query attribute for the browse
        • Access the browse columns
        • Lock columns
        • Move columns
      • Browse style options
        • Use stacked labels
        • Justify labels
        • Indicate column sort order
        • Use color to distinguish updateable columns
        • Use color and font to distinguish cells
        • Establish ToolTip information
        • Use a disabled updateable browse as a read-only browse
    • Resizable browse objects
      • Resize the browse
      • Move the browse
      • Resize the browse column
      • Move the browse column
      • Change the row height of the browse
      • Additional browse attributes
      • User manipulation events for a browse
    • Use browse objects in character interfaces
      • Character browse modes
      • Character browse control keys
      • Functional differences from the Windows graphical browse
    • Conclusion
  • Advanced Use of Procedures in ABL
    • RETURN statement and RETURN-VALUE
    • Persistent procedures
      • Run a procedure PERSISTENT
        • THIS-PROCEDURE system handle
        • Instantiate the persistent procedure
        • Parameters and persistent procedures
      • Use a persistent procedure as a run-time library
        • Obtain a procedure handle for a persistent procedure
        • Use the SESSION handle to locate running procedures
      • Useful procedure attributes and methods
      • Use a persistent procedure as shared code
      • Use a persistent procedure to duplicate code
      • Delete persistent procedures
      • Examples: Communicate between persistent procedures
      • Use SOURCE-PROCEDURE to identify your caller
    • Shared and global objects in ABL procedures
      • Why you generally should not use shared objects
      • Global shared objects in ABL
        • When to consider using global shared objects
  • Define Functions and Build Super Procedures
    • User-defined functions
      • Define a function
        • Make a forward declaration for a function
          • Make a local forward declaration
          • Make a declaration of a function in another procedure
        • Use the AppBuilder to generate function definitions
        • Make run-time references with DYNAMIC-FUNCTION
    • Use super procedures in your application
      • Super procedure language syntax
        • Change the order of super procedures
        • Invoke behavior in super procedures
      • Super procedure guidelines
        • Guideline 1: Use a single super procedure stack
        • Guideline 2: Use TARGET-PROCEDURE to refer back to the object procedure
        • Guideline 3: Make super procedure code shareable
        • Guideline 4: Avoid defining object properties and events in super procedures
        • Guideline 5: Never run anything directly in a super procedure
      • Use session super procedures
      • Super procedure example
    • PUBLISH and SUBSCRIBE statements
      • Subscribe to an event
      • Publish an event
      • Pass parameters
      • Cancel a subscription
      • PUBLISH/SUBSCRIBE example
    • Conclusion
  • Handle Data and Lock Records
    • Overview of data handling statements
    • Record locking in ABL
      • Record locking examples
        • Use EXCLUSIVE-LOCKs
        • Use and upgrade SHARE-LOCKS
        • Use the NO-WAIT Option with the AVAILABLE and LOCKED functions
        • Read records with NO-LOCK
      • Make sure you release record locks
      • Optimistic and pessimistic locking strategies
        • Use FIND CURRENT and CURRENT-CHANGED
  • Update Your Database and Write Triggers
    • Transactions in ABL
    • Build updates into an application
    • Define database triggers
      • Database trigger guidelines
      • Database events
      • Trigger procedure headers
      • Access and define triggers
        • Use database sequences in CREATE triggers
      • Session triggers
      • General trigger considerations
  • Manage Transactions
    • Control the size of a transaction
      • Make a transaction larger
      • Make a transaction smaller
      • Transactions and trigger and procedure blocks
      • The NO-UNDO keyword on temp-tables and variables
    • Use the UNDO statement
      • Use the UNDO statement in the sample procedure
        • Use UNDO, LEAVE on a block
      • Subtransactions
      • Transaction mechanics
      • Use the ON phrase on a block header
      • Handle the ERROR condition
        • ERROR-STATUS system handle
        • RETURN statement and ON . . . RETURN phrase
      • ENDKEY condition
      • STOP condition
        • System and software failures
      • QUIT condition
  • Use Dynamic Graphical Objects
    • Create visual objects
    • Object handles
      • Static object handles
    • Manage dynamic objects
      • Delete dynamic objects
      • Use widget pools
      • Use named widget pools
      • Use unnamed widget pools
      • Are widget pools static or dynamic objects?
      • Manipulate the objects in a window
      • Identify the columns of a browse
      • Use the CAN-QUERY and CAN-SET functions
      • Add dynamic objects to a window
      • Use a buffer handle and buffer field handles
      • Populate a list at run time
      • Create dynamic fields
      • Add dynamic fields to the test window
        • Storing a list of handles
        • Use the FONT-TABLE to make the labels colon-aligned
        • Field format versus width
      • Use multiple windows
        • Window system handles
        • Useful window attributes, methods, and events
        • WINDOW-CLOSE event example
      • Create window families
    • Create a dynamic browse
      • Add columns to the browse
      • Notes on dynamic browses and browse columns
      • Extend the sample procedure with a dynamic browse
      • Access the browse columns
    • Create a dynamic menu
      • Navigate the hierarchy of menu handles
      • Add a dynamic menu to the test window
    • Summary
  • Use Dynamic Queries and Buffers
    • Use dynamic queries and query handles
      • Dynamic query methods and attributes
      • Dynamic query navigation methods
      • Dynamic query reposition methods
      • Other dynamic query attributes
      • Clean up a dynamic query
      • Extend the sample window to filter dynamically
        • Define a trigger block for multiple objects
        • Use the SELF handle to identify an object in a trigger
        • Use INDEX-INFORMATION and a MESSAGE with a yes/no answer
    • Use dynamic buffers and buffer handles
      • Buffer handle attributes
      • Buffer handle methods
      • Specify the lock or wait option as a variable
      • Extend the test window to use a buffer handle
      • Clean up dynamic buffers
      • Special dynamic buffer considerations
      • Use buffer-field objects
      • Export and import data dynamically using buffers
  • Create and Use Dynamic Temp-Tables and Browses
    • Create and use dynamic temp-tables
      • Temp-table methods
        • CREATE-LIKE() method
        • CREATE-LIKE-SEQUENTIAL() method
        • ADD-FIELDS-FROM() method
        • ADD-LIKE-FIELD() method
        • ADD-NEW-FIELD() method
        • ADD-LIKE-INDEX() method
        • ADD-NEW-INDEX() method
        • ADD-INDEX-FIELD() method
        • TEMP-TABLE-PREPARE() method
        • CLEAR() method
      • Temp-table attributes
      • Temp-table buffer methods and attributes
      • Change field attributes in a temp-table buffer
      • Use shorthand syntax for dynamic field and table references
      • Clean up a dynamic temp-table
      • Extend the example to create and display records
    • Use a temp-table as a parameter
      • Use the TABLE form
        • Pass the TABLE within a session
        • Pass the TABLE between sessions
      • Use the HANDLE form
      • Use the TABLE-HANDLE form
      • Possible combinations of temp-table parameter definitions
      • Pass temp-tables by reference or by binding
        • TABLE-HANDLE form with BY-REFERENCE
        • TABLE-HANDLE form with BIND
    • Create and use dynamic browses
      • Add columns to the browse
      • Extend the test procedure with a dynamic browse
        • Browse columns and validation expressions
      • Add columns to a static browse
      • Query methods for use with browses
    • The dynamic CALL object
    • Dynamic programming considerations
  • ABL Programming Best Practices
    • Write efficient procedures
      • Use NO-UNDO variables and temp-tables
      • Group assignments with the ASSIGN statement
      • Use BUFFER-COPY and BUFFER-COMPARE
      • Block-related tips
      • The CASE statement
      • Use arrays instead of lists
      • Use FOR FIRST versus FIND
      • The ETIME function
      • Use the CAN-FIND function
      • Dynamic programming tips
      • Use indexes properly
      • Hide screen contents when making changes
      • Define efficient queries and FOR EACH statements
      • Pass temp-tables as parameters
      • Set your Propath correctly
      • Database and application server-related issues
      • Configure your session with startup options
    • Memory management in ABL
      • Clean up dynamically allocated memory
      • Use widget pools
      • Make the best use of dynamic objects
      • Avoid stale handles
      • Delete persistent procedures
      • Delete temp-table copies
      • Other object types
    • Conclusion to the book
Table of Contents

Create nested blocks to display related data

Save PDF
Save selected topicSave selected topic and subtopicsSave all topics
Share
Share to emailCopy topic URL
Print
  • Last Updated: January 22, 2026
  • 5 minute read
    • OpenEdge
    • Version 12.8
    • Documentation

To review, the FOR EACH statement in your procedure creates a code block nested inside the implicit main block of the procedure itself. Now you create a second FOR EACH block, nested inside the first FOR EACH, to display the Order records in the database for each New Hampshire Customer.

To create a nested block, add another FOR EACH block inside the first block, so that your procedure looks like this:

FOR EACH Customer NO-LOCK WHERE Customer.State = "NH" BY Customer.City:
  DISPLAY Customer.CustNum Customer.Name Customer.City.
  FOR EACH Order OF Customer NO-LOCK:
    DISPLAY Order.OrderNum Order.OrderDate Order.ShipDate.
  END.
END.

This example shows the code indented so that the new block is visually nested in the outer block, which helps code readability.

First, look at the new FOR EACH statement. The keyword OF is a shorthand for a WHERE clause that joins the two tables together. When you looked at the two tables and their fields in the Data Dictionary, you saw that both tables have a CustNum field. This is the primary key of the Customer table, which means that each Customer is assigned a unique number for the CustNum field, and this is the primary identifier for the Customer. In the Order table, the OrderNum is the unique Order identifier, and its primary key. The CustNum field in the Order table points back to the Customer the Order is for. It is a foreign key because it points to a record in another table. To retrieve and display the Orders for a Customer, you have to join the two tables on the CustNum field that they have in common. The full WHERE clause for this join would be: WHERE Customer.CustNum = Order.CustNum. This kind of syntax will be familiar to you if you have ever worked with SQL.

The WHERE clause is telling the ABL Virtual Machine (AVM) to select those records where the CustNum field in one table matches the CustNum field in the other. In order to tell the AVM which field is which, both are qualified by the table name, followed by a period.

In ABL, you can use the syntax Order OF Customer as a shorthand for this join if the two tables have one or more like-named fields in common that constitute the join relationship, and those fields are indexed in at least one of the tables (normally they should be indexed in both). You can always use the full WHERE clause syntax instead of the OF phrase if you wish; the effect is the same, and if there is any doubt as to how the tables are related, it makes the relationship your code is using completely clear. In fact, the OF phrase is really one of those beginner shortcuts that makes it easy to write a very simple procedure but which really is not good practice in a complex application, because it is not clear just from reading the statement which fields are being used to relate the two tables. You should generally be explicit about your field relationships in your applications.

These simple nested FOR EACH statements accomplish something that would be a lot of work in other languages. To retrieve the Customers and their Orders separately, as you really want to do, you would have to define two separate queries using embedded SQL syntax, open them, and control them explicitly in your code. This would be a lot of work. For example, the straight forward single SQL query to retrieve the same data would be:

SELECT Customer.CustNum, Name, City, OrderNum, OrderDate, ShipDate FROM
Customer, Order WHERE Customer.State = "NH" AND Customer.CustNum = Order.CustNum;

This code would retrieve all the related Customers and Orders into a single two-dimensional table, which is not very useful: all the Customer information would be repeated for each of the Customer's Orders, and you would have to pull it apart yourself to display the information as header and detail, which is probably what you want. By contrast, when you run your very simple ABL procedure, you get a default display that represents the data properly as master (Customer) and detail (Order) information, as shown in the following figure.

Figure 1. Result of running simple sample procedure

The AVM automatically gives you two separate display areas, one for the Customer fields showing one Customer at a time, and one for the Orders of the Customer. These display areas are called frames. You learn more about ABL frames in later topics.

Unlike in the first example, which displays a whole page of Customers, each time you press the SPACE BAR, the AVM displays just the next Customer and its Orders. Why did the AVM do this? The nested FOR EACH blocks tell the AVM that there are multiple Orders to display for each Customer, so it knows that it does not make sense to display more than one Customer at a time. So it creates a small frame just big enough to display the fields for one Customer, and then separately creates another frame where it can display multiple Orders. The latter frame is called a down frame, because it can display multiple rows of data as it moves down the page. The top frame is actually referred to as a one down frame because it displays only a single row of data at a time.

You can control the size of the frames, how many rows are displayed, and many other attributes, by appending a WITH phrase to your statement.

To see how the WITH phrase can affect your test procedure:

  1. Add the words WITH CENTERED to the DISPLAY statement for the Order frame:
    FOR EACH Customer NO-LOCK WHERE Customer.State = "NH" BY Customer.City:
      DISPLAY Customer.CustNum Customer.Name Customer.City.
      FOR EACH Order OF Customer NO-LOCK:
        DISPLAY Order.OrderNum Order.OrderDate Order.ShipDate WITH CENTERED.
      END.
    END.
  2. Run the procedure again. The Order frame is centered in the default display window:

There are lots of frame attributes you can specify here (for a description of frame attributes, read the section on the Frame Phrase in the ABL Reference). This book does not tell you much more about frame attributes, either now or later, because you will not use most of them in your GUI applications. These attributes are designed to help you define frames for a character mode application, where the interface is basically just a series of 24 or 25 lines of 80 characters each. For this kind of display format, a sequence of frames displayed one beneath the other is an appropriate and convenient way to lay out the screen. But in a GUI application, you instead lay out your display using a visual design tool, such as the OpenEdge AppBuilder or Progress Developer Studio for OpenEdge and it generates the code or data needed to create the user interface at run time. These topics show you the basics of how ABL works and how it was designed, even though you will do most of your work a different way in your new applications.

TitleResults for “How to create a CRG?”Also Available inAlert