How to Pass Values Between Multiple Forms in Oracle Forms

Passing data from one form to another is a common requirement in real projects. In this tutorial, you will learn practical, safe, and repeatable ways to Pass Values Between Multiple Forms in Oracle Forms using parameter lists, global variables, and a few supporting patterns. Clear, copy-pasteable examples are included so you can adapt them quickly.

Why you need to pass values across forms

Complex applications rarely live in a single form. You might open a Customer Details form from a Customer Search form, jump to a Line Items form from a Sales Order header, or return a selection from a picklist. Each of these flows requires you to share context—IDs, modes, dates, or flags—so the next screen can load the right records immediately and behave correctly.

The three dependable approaches

  1. Parameter lists (recommended): Create a PARAMLIST, add typed parameters, and invoke the next form with CALL_FORM, NEW_FORM, or OPEN_FORM. The target form reads values via :PARAMETER.<name> or NAME_IN('<name>').
  2. GLOBAL variables (simple, but be disciplined): Assign values to :GLOBAL.* before navigation and read them in the next form. Clear them when done to avoid stale data or name collisions.
  3. Persisted state (advanced, selective use): Use a temporary table or a controlled server-side package/table when you need data to survive across sessions or long flows. This is heavier and should be applied sparingly.

Method 1 — Parameter lists (best practice)

Parameter lists are the most robust way to Pass Values Between Multiple Forms in Oracle Forms. They are explicit, typed, and scoped to a single navigation event.

Step 1: Define form parameters in the target form

In the target form, create form-level parameters (Object Navigator → Parameters), for example:

  • P_CUSTOMER_ID (Char)
  • P_RUN_MODE (Char, e.g., VIEW or EDIT)

You will reference these as :PARAMETER.P_CUSTOMER_ID and :PARAMETER.P_RUN_MODE.

Step 2: Create and populate a parameter list in the source form

Attach this to a button (e.g., “Open Details”) in your source form:

DECLARE
  pl_id  PARAMLIST;
BEGIN
  -- Reuse if it exists
  pl_id := GET_PARAMETER_LIST('PL_PASS');
  IF NOT ID_NULL(pl_id) THEN
    DELETE_PARAMETER_LIST(pl_id);
  END IF;

  -- Create fresh list and add typed parameters
  pl_id := CREATE_PARAMETER_LIST('PL_PASS');
  ADD_PARAMETER(pl_id, 'P_CUSTOMER_ID', TEXT_PARAMETER, :BLK_CUSTOMERS.CUSTOMER_ID);
  ADD_PARAMETER(pl_id, 'P_RUN_MODE',   TEXT_PARAMETER, 'EDIT');

  -- Navigate: choose one of the following invocations
  -- CALL_FORM keeps the calling form in memory and returns here when child closes
  CALL_FORM('CUSTOMER_DETAIL', NO_HIDE, DO_REPLACE, pl_id);

  -- Alternatively:
  -- NEW_FORM('CUSTOMER_DETAIL', NO_HIDE, DO_REPLACE, pl_id); -- replaces the current form
  -- OPEN_FORM('CUSTOMER_DETAIL', ACTIVATE, SESSION, pl_id);   -- opens in a new session/window
END;

Key notes:

  • GET_PARAMETER_LIST/DELETE_PARAMETER_LIST avoid “already exists” errors.
  • Use TEXT_PARAMETER, NUMBER_PARAMETER, or DATE_PARAMETER appropriately.
  • Pick CALL_FORM if you need to return to the parent form once the child closes.

Step 3: Read parameters in the target form

In the target form, use WHEN-NEW-FORM-INSTANCE to consume the parameters and load data:

DECLARE
  v_customer_id VARCHAR2(50);
  v_mode        VARCHAR2(10);
BEGIN
  v_customer_id := :PARAMETER.P_CUSTOMER_ID; -- or NAME_IN('P_CUSTOMER_ID')
  v_mode        := :PARAMETER.P_RUN_MODE;

  IF v_customer_id IS NOT NULL THEN
    :BLK_DETAIL.CUSTOMER_ID := v_customer_id;
    -- Optionally execute a query to fetch the record immediately
    GO_BLOCK('BLK_DETAIL');
    EXECUTE_QUERY;
  END IF;

  -- Honor the mode
  IF v_mode = 'VIEW' THEN
    SET_BLOCK_PROPERTY('BLK_DETAIL', UPDATE_ALLOWED, PROPERTY_FALSE);
    SET_BLOCK_PROPERTY('BLK_DETAIL', INSERT_ALLOWED, PROPERTY_FALSE);
    SET_BLOCK_PROPERTY('BLK_DETAIL', DELETE_ALLOWED, PROPERTY_FALSE);
  END IF;
END;

Tips:

  • Parameter names are case-sensitive; match exactly in both forms.
  • Remember parameter values are character data when read via :PARAMETER; convert explicitly if needed.

Choosing between CALL_FORM, NEW_FORM, and OPEN_FORM

  • CALL_FORM: Opens the next form and returns control to the caller when it closes. The caller remains in memory (modal flow). Best for picklists or drill-down-and-return flows.
  • NEW_FORM: Closes the current form and opens the next one (replaces memory). Best for linear flows (e.g., a wizard-like transition).
  • OPEN_FORM: Opens another form instance in a separate session/window. Good for side-by-side work or dashboards.

Method 2 — GLOBAL variables (simple and effective for “return” flows)

GLOBAL variables are easy and useful when the child form must send a value back to the parent after a CALL_FORM.

Pass to child form

:GLOBAL.G_CUSTOMER_ID := :BLK_CUSTOMERS.CUSTOMER_ID;
CALL_FORM('CUSTOMER_DETAIL', HIDE, DO_REPLACE);

In the child form:

BEGIN
  IF :GLOBAL.G_CUSTOMER_ID IS NOT NULL THEN
    :BLK_DETAIL.CUSTOMER_ID := :GLOBAL.G_CUSTOMER_ID;
    GO_BLOCK('BLK_DETAIL');
    EXECUTE_QUERY;
  END IF;
END;

Return a value from child to parent

In the child form (e.g., a Search/Select form), when user picks a row:

:GLOBAL.G_SELECTED_CUSTOMER := :BLK_SEARCH.CUSTOMER_ID;
EXIT_FORM;

The code after CALL_FORM in the parent continues, so you can read the global:

IF :GLOBAL.G_SELECTED_CUSTOMER IS NOT NULL THEN
  :BLK_CUSTOMERS.CUSTOMER_ID := :GLOBAL.G_SELECTED_CUSTOMER;
  GO_BLOCK('BLK_CUSTOMERS');
  EXECUTE_QUERY;
  -- Clean up
  ERASE('GLOBAL.G_SELECTED_CUSTOMER'); -- or set to NULL
END IF;

Best practices for GLOBALs:

  • Use a clear naming convention (prefix with G_).
  • Always clear them after use (ERASE('GLOBAL.G_X')).
  • Avoid passing sensitive data.
  • Prefer parameter lists for one-way input; prefer GLOBALs to return a result from a called form.

Validating and converting data safely

  • Convert types explicitly:
    :BLK_DETAIL.ORDER_ID := TO_NUMBER(:PARAMETER.P_ORDER_ID);
  • Guard against nulls:
    IF NVL(:PARAMETER.P_CUSTOMER_ID, '') <> '' THEN … END IF;
  • Validate before navigation to avoid unnecessary round-trips:
    IF :BLK_CUSTOMERS.CUSTOMER_ID IS NULL THEN MESSAGE('Select a customer first.'); RAISE FORM_TRIGGER_FAILURE; END IF;

A complete mini-scenario

Goal: From Customer Search form, open Customer Detail form in VIEW mode with the selected CUSTOMER_ID, then optionally return a picked value.

In Search form (button WHEN-BUTTON-PRESSED):

DECLARE
  pl_id PARAMLIST;
BEGIN
  IF :BLK_SEARCH.CUSTOMER_ID IS NULL THEN
    MESSAGE('Please select a customer.'); 
    RAISE FORM_TRIGGER_FAILURE;
  END IF;

  pl_id := GET_PARAMETER_LIST('PL_CUST');
  IF NOT ID_NULL(pl_id) THEN
    DELETE_PARAMETER_LIST(pl_id);
  END IF;

  pl_id := CREATE_PARAMETER_LIST('PL_CUST');
  ADD_PARAMETER(pl_id, 'P_CUSTOMER_ID', TEXT_PARAMETER, :BLK_SEARCH.CUSTOMER_ID);
  ADD_PARAMETER(pl_id, 'P_RUN_MODE',   TEXT_PARAMETER, 'VIEW');

  CALL_FORM('CUSTOMER_DETAIL', NO_HIDE, DO_REPLACE, pl_id);

  -- If the detail form returned a choice (optional)
  IF :GLOBAL.G_SELECTED_CUSTOMER IS NOT NULL THEN
    :BLK_SEARCH.CUSTOMER_ID := :GLOBAL.G_SELECTED_CUSTOMER;
    GO_BLOCK('BLK_SEARCH');
    EXECUTE_QUERY;
    ERASE('GLOBAL.G_SELECTED_CUSTOMER');
  END IF;
END;

In Detail form (WHEN-NEW-FORM-INSTANCE):

DECLARE
  v_id   VARCHAR2(50) := :PARAMETER.P_CUSTOMER_ID;
  v_mode VARCHAR2(10) := :PARAMETER.P_RUN_MODE;
BEGIN
  IF v_id IS NOT NULL THEN
    :BLK_DETAIL.CUSTOMER_ID := v_id;
    GO_BLOCK('BLK_DETAIL');
    EXECUTE_QUERY;
  END IF;

  IF v_mode = 'VIEW' THEN
    SET_BLOCK_PROPERTY('BLK_DETAIL', UPDATE_ALLOWED, PROPERTY_FALSE);
    SET_BLOCK_PROPERTY('BLK_DETAIL', INSERT_ALLOWED, PROPERTY_FALSE);
    SET_BLOCK_PROPERTY('BLK_DETAIL', DELETE_ALLOWED, PROPERTY_FALSE);
  END IF;
END;

In Detail form (optional “Choose” button):

:GLOBAL.G_SELECTED_CUSTOMER := :BLK_DETAIL.CUSTOMER_ID;
EXIT_FORM;

This end-to-end pattern covers the most frequent business flows with clear control of direction (to child via PARAMLIST, back to parent via GLOBAL).

Troubleshooting checklist

  • Parameter name mismatch: Ensure names and case are identical in ADD_PARAMETER and :PARAMETER.<name>.
  • Reused list conflict: Always delete existing lists with the same name before CREATE_PARAMETER_LIST.
  • Empty queries in target: Confirm you set the key item before EXECUTE_QUERY and that the block has a proper WHERE clause or key mapping.
  • Stale GLOBAL values: Clear them after use to prevent surprises on later navigations.
  • Wrong invocation choice: Use CALL_FORM when you must return to the caller; use NEW_FORM for linear replacement; use OPEN_FORM for parallel work.

Best practices (my opinionated shortlist)

  • Prefer parameter lists for forward navigation—they are explicit, typed, and self-documenting.
  • Use GLOBALs only to return a small value from a called form, and always clear them.
  • Keep parameter names business-meaningful, like P_CUSTOMER_ID, P_RUN_MODE, P_SOURCE_FORM.
  • Centralize constants (e.g., run modes) in a common package or in form-level constants to avoid typos.
  • Validate early and fail fast with clear messages.

Conclusion

To Pass Values Between Multiple Forms in Oracle Forms reliably, make parameter lists your default, use GLOBALs sparingly for return flows, and choose the right navigation method (CALL_FORM, NEW_FORM, OPEN_FORM) for the user experience you want. With the patterns and code provided, you can wire multi-form navigation that is predictable, secure, and easy to maintain.

Vinish Kapoor
Vinish Kapoor

Vinish Kapoor is a seasoned software development professional and a fervent enthusiast of artificial intelligence (AI). His impressive career spans over 25+ years, marked by a relentless pursuit of innovation and excellence in the field of information technology. As an Oracle ACE, Vinish has distinguished himself as a leading expert in Oracle technologies, a title awarded to individuals who have demonstrated their deep commitment, leadership, and expertise in the Oracle community.

guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments