Skip to main content

The Hidden Complexity of "Just Add a Save Button"

The Problem: Saving Data Isn't Always Simple

At first glance, saving user input seems straightforward: provide a "Save" button and store the data when clicked. Alternatively, one might consider an auto-save approach, ensuring that changes are immediately committed. However, real-world applications introduce complexities that make this decision far from trivial.

The Specific Problem We’re Facing

Our application layout consists of:

  • A tree navigation panel on the left, where users can select an item.
  • A three-tab content area on the right, where each tab contains multiple fields.

Image of the UI

  • Each tab may write to different API endpoints, as the data is related but not necessarily stored in the same place.
  • The layout within each tab can vary depending on the type of selected item (the tree contains different entity types).
  • Selecting a new item in the tree navigation loads data into all three tabs, meaning a context switch must be handled carefully.

This setup introduces the challenge of handling unsaved changes when switching items while ensuring users don’t accidentally lose data.

Undo/Redo Complexity

One additional challenge is that our text inputs include undo and redo buttons, which function based on local state. However, this introduces a conflict:

  • If we reload the component, the undo/redo history is lost.
  • Auto-saving changes on tab switch would mix behavior, where some fields might retain local undo history while others would be committed permanently.
  • To resolve this, we would need to override the default q-input undo/redo functionality and implement our own custom state tracking to persist history even when the component reloads.

Evaluating the Possible Solutions

1. Disable All Navigation Until Changes Are Saved or Discarded

  • Description: Prevent both tab switches and selecting a new item until the user explicitly saves or discards changes.
  • Pros: Preserves the default undo/redo functionality.
  • Cons: Users cannot freely switch between tabs, potentially needing another browser tab or reference to view other sections.

2. Allow Tab Switching, Disable Selecting a New Item Until Saved or Discarded

  • Description: Users can switch between tabs but cannot select a different item in the tree until they save or discard changes.
  • Pros: Allows users to navigate within an item’s sections while preserving their work.
  • Cons: Undo/redo functionality would need to be modified:
    • Removing undo/redo functionality.
    • Keeping undo/redo but having it be non-functional across component reloads (tab switches).
    • Writing a custom implementation to persist undo history across component reloads (could be complex... choices here as well).

3. Autosave on Change

  • Description: Automatically save changes as they are made, likely with a debounce/throttle mechanism to avoid excessive API calls.
  • Pros: Simple to implement, eliminates the need for manual saving.
  • Cons:
    • No built-in undo/redo functionality unless implemented separately.
    • Increased API calls (though debouncing mitigates this).
    • Users may accidentally clear a field and immediately lose data, making versioning important.
    • Would require a versioning system in the backend to allow retrieval of previous states (though we do plan on adding a global object history, this is not yet exposed via API).

4. Remove Tabs and Use a Single Scrollable Page

  • Description: Instead of tabs, display all related sections in a single scrollable view, reducing component reloads.
  • Pros:
    • Avoids undo/redo issues caused by component reloads due to tab switching
      • Component reloads due to page switching still cause undo/redo to reset
    • Allows users to see all information at once.
    • Less complexity around handling unsaved changes across tabs.
  • Cons:
    • May be visually overwhelming, requiring UI adjustments.
    • The issue of handling unsaved changes when selecting a new item or page changes remains.
    • Navigation within the page might need additional enhancements (e.g., floating quick-access menu).

Our Decision: To Be Determined

We are currently discussing these options with the SME to determine the best approach. Each option presents trade-offs between UX simplicity and technical feasibility.

Lessons Learned & Takeaways

  • Users don’t care where data is stored—until it doesn’t align with their expectations.
  • Engineering decisions shape UX, whether we like it or not.
  • Technical simplicity is valuable, but UX consistency is critical.
  • Mixing auto-save with undo/redo requires careful handling to avoid unexpected behavior.
  • Navigational constraints are critical when dealing with unsaved changes, but balancing flexibility and safety is key.

By thinking through these challenges carefully, we aim to ensure that users have both control and safety in their interactions with our application, while keeping the backend implementation manageable. The complexity is hidden—but it’s there, working to create a seamless experience.