Skip to main content

Command Palette

Search for a command to run...

Improving User Experience (UX) in Oracle APEX

From Functional to Fantastic: Tricks with JavaScript, Dynamic Actions, and Page Items to create modern applications.

Updated
8 min read
Improving User Experience (UX) in Oracle APEX

🇪🇸 Leer en Español

💼 For IT Managers & Decision Makers

  • Business Problem: High user error rates and clumsy interfaces lead to customer churn and ballooning support costs; a premium, responsive UX improves user retention and speeds up workflow execution.

  • Operational Efficiency: Relying on standardized APEX JavaScript APIs and Dynamic Actions instead of custom DOM manipulation reduces application maintenance overhead by 45%.

  • Developer Action: If you are a developer, skip directly to the code below!


Introduction: From Functional to Fantastic

You've mastered the basics (Best Practices) and bulletproofed your app (Security). Now, it's time to go for the WOW factor: the User Experience (UX).

In Oracle APEX, great UX isn't just about "looking pretty"; it's about perceived performance, fluid interactions, and that feeling of using world-class software. In this APEX Insights post, we are going to transform a functional app into an exceptional experience.

📋 The Case Study: Redesigning a 20-Field Legacy Nightmare

To ground these concepts, we will follow a real-world scenario. A logistics client asked us to audit their Sales Invoice Creator screen. The legacy implementation was a massive, single-region form with 20 unorganized fields.

Every field change triggered a database round-trip via individual Dynamic Actions. There were no loading indicators, leading to severe layout shifts, double-submissions, and extreme user frustration. Keyboard navigation was entirely broken, forcing users to repeatedly grasp their mouse just to jump across broken sections.

Here is how we refactored this nightmare into a dynamic, blazing-fast, and accessible 3-step reactive experience using the APEX JavaScript API and optimized Dynamic Actions.


1. Mastering Dynamic Actions

Dynamic Actions (DAs) are the declarative bridge between the Frontend (JavaScript) and the Backend (PL/SQL). However, having 20 separate Dynamic Actions executing on a single page is a recipe for maintenance chaos and sluggish performance.

Trick 1: When to Use DAs vs. Pure JavaScript

Scenario Recommendation Reason
Simple Interactions Dynamic Actions Less code, faster declarative setup.
Complex Biz Logic PL/SQL (via DA Server-Side) Business rules must live in the database.
DOM Manipulation / Custom UI Pure JavaScript (Page Level) Best for event handling and complex browser UI behaviors.

Trick 2: Optimize Fire on Initialization to Prevent Layout Shifts

In the legacy form, almost half of the 20 fields were hidden or disabled by default under specific conditions (e.g., Shipping Address only showing when Custom Delivery is selected).

Having 10 individual Dynamic Actions with Fire on Initialization = True caused the page to load, render the fields, and then abruptly hide them milliseconds later. This created a jarring layout shift.

  • The Refactored Approach: We disabled Fire on Initialization on individual DAs. Instead, we consolidated the page setup rules into a single JavaScript initialization function configured at the page level and executed on Page Load.
// A single consolidated Page Load initialization routine
function initInvoiceForm() {
  const isCustomDelivery = $v("P1_DELIVERY_TYPE") === "CUSTOM";

  // Declaratively show/hide using APEX API rather than DOM styling
  if (isCustomDelivery) {
    apex.item("P1_SHIPPING_ADDRESS").show();
  } else {
    apex.item("P1_SHIPPING_ADDRESS").hide();
  }
}

// Executed once on Page Load
initInvoiceForm();

2. Perceived Performance with Visual Feedback

Perceived performance is how fast the user feels the application responds. When our invoice screen had to fetch customer details and current price lists from the server, the interface would freeze for 1.5 seconds. Users frequently double-clicked buttons, generating duplicate invoices.

Trick 3: Always Use a Spinner for AJAX Calls

When running a background PL/SQL process via AJAX, always give the user immediate visual feedback.

// Refactored AJAX Call with target-specific Spinner
const invoiceRegion = "#invoice_details_region";

// Show spinner directly over the affected region
const mySpinner = apex.util.showSpinner($(invoiceRegion));

apex.server.process(
  "FETCH_CUSTOMER_DEALS",
  { x01: $v("P1_CUSTOMER_ID") },
  {
    success: function (pData) {
      // Populate fields instantly
      $s("P1_DISCOUNT_RATE", pData.discount);
      calculateInvoiceTotals();
    },
    complete: function () {
      // Remove the spinner upon completion
      mySpinner.remove();
    },
  },
);

Trick 4: Instant Success Notifications

After completing a step or successfully submitting the invoice draft, avoid reloading the entire page. Instead, provide clear, asynchronous feedback.

  • Use apex.message.showPageSuccess('Invoice draft saved successfully!'); to display a native, floating notification.

  • Use apex.message.clearErrors(); and apex.message.showErrors() for non-intrusive form validation alerts.


3. Advanced Interactions with the APEX JavaScript API

Direct DOM manipulation is the fastest route to high technical debt in Oracle APEX.

Trick 5: Forget document.getElementById and jQuery Selectors for Values

⚠️ WARNING: Directly manipulating page item DOM nodes (e.g., document.getElementById('P1_TOTAL').value = 100) bypasses APEX's state management. APEX won't register the change, leading to session state mismatch and data loss.

Always use the native APEX JavaScript API wrapper apex.item().

Task Legacy / Vulnerable Code Modern APEX API
Get Value document.getElementById('P1_QTY').value $v('P1_QTY')
Set Value document.getElementById('P1_TOTAL').value = '10' $s('P1_TOTAL', '10')
Disable $('#P1_ITEM').prop('disabled', true) apex.item('P1_ITEM').disable()
Hide Component $('#P1_ITEM').hide() apex.item('P1_ITEM').hide()

Trick 6: Clean Client-Side Multiplications

In our redesigned form, the invoice lines recalculate instantly on the client side as the user types, using standard API shortcuts:

function calculateInvoiceTotals() {
  const qty = parseFloat($v("P1_QUANTITY")) || 0;
  const price = parseFloat($v("P1_UNIT_PRICE")) || 0;
  const discount = parseFloat($v("P1_DISCOUNT_RATE")) || 0;

  const subtotal = qty * price;
  const total = subtotal * (1 - discount / 100);

  // Safely update values using the APEX shortcut
  $s("P1_SUBTOTAL", subtotal.toFixed(2));
  $s("P1_TOTAL", total.toFixed(2));
}

4. Keyboard Navigation & Focus Management

When building highly dynamic client-side experiences, it is easy to leave keyboard and screen-reader users behind. For example, if you hide a section of a form or dynamically switch between steps, a keyboard user navigating via Tab can easily lose their place or get kicked back to the top of the page.

The Focus Golden Rule: Never Leave the Focus Floating

Always ensure that any client-side layout shift (such as displaying/hiding elements or showing validation banners) programmatically guides the keyboard cursor to the most logical next action.

Trick 7: Redirecting Focus Programmatically

When a user completes a step or activates a dynamic panel, use the APEX JS API to transfer the keyboard focus seamlessly.

// Programmatically shift focus to the shipping zip input
apex.item("P1_SHIPPING_ZIP").setFocus();

If you are displaying dynamic inline errors or success messages via JavaScript, move the focus to the alert container so it is read immediately by screen readers:

// Focus on the alert region so it is registered by assistive tech
$("#apex_layout_alert").focus();

To learn more about keyboard traps, semantic HTML, and ARIA attributes, explore our comprehensive guide on Accessibility (A11y) in Oracle APEX.


5. Strategy with Page Items: Reduce the Round-Trip

The round-trip (network request to the database) is the biggest enemy of speed and UX. While APEX makes it extremely easy to run processes, it's easy to overuse them.

Trick 8: Clear State Locally

When resetting our invoice line items, the legacy page executed a PL/SQL process calling APEX_UTIL.CLEAR_PAGE_CACHE which made a full server call just to empty some textboxes.

  • UX Alternative: We replaced this with a client-side Dynamic Action running a JavaScript snippet that resets values instantly with zero latency.
// Loop through and clear items on the client side instantly
["P1_ITEM_ID", "P1_QUANTITY", "P1_UNIT_PRICE", "P1_TOTAL"].forEach(
  function (itemName) {
    apex.item(itemName).setValue(null);
  },
);

🔗 Going Deeper

Ready to take your frontend skills to the next level? Explore these articles in the series:


Conclusion

The difference between a "good" and an excellent APEX application lies in the details. By moving away from bloated, uncoordinated Dynamic Actions, implementing target-specific visual feedback, utilizing native JavaScript APIs, managing keyboard focus, and keeping calculations client-side, we transformed a painful 20-field form into a fluid, responsive workflow. Your users don't just want functional screens—they want experiences they love to use.

If your users constantly complain that your APEX application feels like an outdated spreadsheet, you don't need to rewrite the entire system; you need a UX intervention.

Stop building static tables. Start building high-performance experiences.

References

  1. APEX JavaScript API Guide (v24.2)

  2. Dynamic Actions Documentation

  3. Oracle APEX Universal Theme Accessibility Standards

Which of these UX practices has made the biggest difference in your enterprise apps? Share your feedback in the comments!


🚀 Elevate Your APEX User Experience

Is a clunky, sluggish interface slowing down your business workflows or frustrating your users? I help enterprise teams design premium, highly reactive, and accessible Oracle APEX applications.