How to Refresh an Open Form with WHEN-FORM-NAVIGATE

Bring an open Oracle Forms 14.1.2 form to the front and let it catch up with a record chosen in another form, instead of opening a second copy.

In an application of several open forms, the user chooses a patient in one form and opens the appointments form for that patient. Later the user chooses another patient and clicks Appointments again. If the appointments form is still open, it should not open a second copy, and it must not keep showing the first patient.

This guide shows how to handle that in Oracle Forms 14.1.2: open a form or bring the open one to the front with GO_FORM, and let the form catch up in its WHEN-FORM-NAVIGATE trigger.

Sample Form for This Guide

The examples and screenshots use the sample forms CW_PATIENTS, CW_APPOINTMENTS, and CW_BILLING from the Oracle Forms code repository on GitHub, with the PL/SQL library cw_lib.pll attached. Download them, open them in Forms Builder, and connect as CAREWELL to follow along.

FormFileWhat it shows
CW_PATIENTSforms/ch40/cw_patients.fmbChoosing a patient and opening the other forms
CW_APPOINTMENTSforms/ch40/cw_appointments.fmbA form that follows the chosen patient
CW_BILLINGforms/ch40/cw_billing.fmbThe same technique with a fallback when no patient is chosen

The forms run against the CareWell Clinic sample schema, which you install first.

WHEN-NEW-FORM-INSTANCE Runs Only Once

WHEN-NEW-FORM-INSTANCE fires when a form starts. A form that is already open and only brought back to the front does not run it again. Instead, Forms fires WHEN-FORM-NAVIGATE in the form the user enters.

TriggerFires when
WHEN-NEW-FORM-INSTANCEThe form starts
WHEN-FORM-NAVIGATEThe user moves into the form from another open form

So a form that must follow choices made elsewhere calls the same procedure from both triggers.

Pass the Choice Through a Shared Package

The chosen patient is kept in a package variable of an attached library, CW_CTX.PATIENT_ID. Forms opened with SHARE_LIBRARY_DATA share one copy of it, so every open form sees the latest value. See how to pass values between forms in Oracle Forms for how shared library data compares with parameters and globals.

The Appointments button of the patients form passes the current patient.

Example (WHEN-BUTTON-PRESSED trigger on CTL.APPTS):

cw_nav.open_module('cw_appointments', :patients.patient_id);

Open the Form or Go to It

OPEN_MODULE, in the library, records the patient, then checks with FIND_FORM whether the form is open. If not, it opens it with SHARE_LIBRARY_DATA; if it is, GO_FORM brings it to the front.

Example (library CW_LIB, package CW_NAV body):

package body cw_nav is
  procedure start_form(p_user varchar2) is
  begin
    if cw_sec.username is null then            -- not signed in: a form run on its own, for testing
      cw_sec.login(p_user);
    end if;
    cw_sec.apply_menu;
    set_window_property(forms_mdi_window, TITLE,
                        'CareWell Clinic - ' || cw_sec.full_name || ' (' || initcap(cw_sec.app_role) || ')');
  end start_form;

  procedure open_module(p_form varchar2, p_patient_id number default null) is
  begin
    if p_patient_id is not null then
      cw_ctx.patient_id := p_patient_id;       -- shared: every form sees the same package data
    end if;
    if id_null(find_form(upper(p_form))) then
      open_form(p_form, activate, no_session, share_library_data);
    else
      go_form(upper(p_form));                  -- open already: its WHEN-FORM-NAVIGATE catches up
    end if;
  end open_module;
end cw_nav;

An older article covers how to use FIND_FORM and GO_FORM in Oracle Forms on their own.

Catch Up in WHEN-FORM-NAVIGATE

The appointments form compares the patient it shows with the shared one, and queries again only if they differ.

Example (program unit LOAD_PATIENT):

-- shows the appointments of the current patient: when the form starts, and when the user comes back
-- to it after choosing another patient in CW_PATIENTS
procedure load_patient is
begin
  if cw_ctx.patient_id is null then
    message('Choose a patient in Patients first.');
  elsif :ctl.patient_id is null or :ctl.patient_id <> cw_ctx.patient_id then
    :ctl.patient_id := cw_ctx.patient_id;
    select first_name || ' ' || last_name || '  (' || mrn || ')' into :ctl.patient_name
      from patients where patient_id = :ctl.patient_id;
    go_block('APPOINTMENTS');
    execute_query;                       -- asks first whether to save changes for the previous patient
  end if;
end;

Both triggers call it: WHEN-NEW-FORM-INSTANCE after the form's startup code, and WHEN-FORM-NAVIGATE on its own.

Example (WHEN-FORM-NAVIGATE trigger):

load_patient;

A search for Omar found eight patients, and Appointments opened the appointments of the first.

Oracle Forms patients search listing eight patients named Omar with Appointments and Invoices buttons
The patients form, with buttons that open other forms for the current patient.
Oracle Forms appointments form opened for the patient chosen in the patients form
The appointments form, opened for the chosen patient.

Unsaved Changes Are Protected

If the user changed an appointment of the previous patient and did not save, EXECUTE_QUERY in LOAD_PATIENT does not discard the change silently. Forms first asks whether to save it, as it does before any query that would replace changed records.

Handle No Choice at All

The billing form uses the same technique with a fallback. Opened from the dashboard with no patient chosen, it lists every invoice still to be paid; opened from the patients form, it lists that patient's invoices.

Example (program unit LOAD_INVOICES):

-- the invoices of the current patient, or, without one, every invoice still to be paid
procedure load_invoices is
begin
  if nvl(cw_ctx.patient_id, -1) = nvl(:ctl.patient_id, -1) and :system.mode = 'NORMAL'
     and :invoices.invoice_id is not null then
    return;                              -- already showing them
  end if;
  :ctl.patient_id := cw_ctx.patient_id;
  if :ctl.patient_id is null then
    :ctl.showing := 'Invoices to be paid';
    set_block_property('INVOICES', DEFAULT_WHERE, 'status in (''OPEN'', ''PARTIAL'')');
  else
    select 'Invoices of ' || first_name || ' ' || last_name into :ctl.showing
      from patients where patient_id = :ctl.patient_id;
    set_block_property('INVOICES', DEFAULT_WHERE, 'patient_id = :ctl.patient_id');
  end if;
  go_block('INVOICES');
  execute_query;
end;

The first test returns early when the form already shows the right invoices, so coming back to the form costs no query.

Conclusion

An open form does not run WHEN-NEW-FORM-INSTANCE again when the user returns to it, but it does run WHEN-FORM-NAVIGATE. Keep the user's current choice in a package shared with SHARE_LIBRARY_DATA, open a form or bring it back with FIND_FORM and GO_FORM, and call one load procedure from both triggers that queries again only when the choice has changed. One open form then follows the user.

Vinish Kapoor
Vinish Kapoor

An Oracle ACE and software veteran with 25+ years of experience, passionate about AI and IT innovation.

guest

0 Comments
Oldest
Newest Most Voted
00