LearnNewsExamplesServices
Frontmatter
id9413
titleCreate Async Subtree Loading for `Neo.data.TreeStore`
stateClosed
labels
enhancementaicore
assigneestobiu
createdAtMar 9, 2026, 12:33 PM
updatedAtMar 9, 2026, 3:31 PM
githubUrlhttps://github.com/neomjs/neo/issues/9413
authortobiu
commentsCount1
parentIssue9404
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtMar 9, 2026, 3:31 PM

Create Async Subtree Loading for Neo.data.TreeStore

Closed v12.1.0 enhancementaicore
tobiu
tobiu commented on Mar 9, 2026, 12:33 PM

Goal

Enhance the Neo.data.TreeStore to support asynchronous loading of child nodes when a collapsed node is expanded, laying the groundwork for handling massive hierarchical datasets without a full BufferedStore.

Architecture & Mechanics

We will leverage the existing Store.load() mechanics (specifically the opts.append pattern) and Collection.splice() to inject new data into the tree without destroying the current view.

  1. Pre-condition Enforcement:
    • If a node is loaded with expanded: true (or collapsed: false), the payload must contain its direct children. The TreeStore parsing logic should throw a warning if this condition is violated to prevent rendering empty folders.
  2. Count Separation:
    • totalCount (Data Perspective): Represents the total size of the fully expanded tree on the backend (useful for metadata/pagination, but not for the scrollbar).
    • count (UI Perspective): Managed by the Collection. Represents the length of the flattened _items array (only the visible nodes). This drives the GridBody scrollbar height and aria-rowcount.
  3. The Async Expand Flow: When expand(nodeId) is called on a node that does not have its children loaded in the #childrenMap:
    • Set the node's state to isLoading: true (triggers UI spinner).
    • Trigger a backend request for the subtree (e.g., store.load({ params: { parentId: nodeId }, append: true })).
    • Upon data arrival, parse the new subtree and update the internal #childrenMap.
    • Update the node's state: isLoading: false, expanded: true.
    • Execute a synchronous splice to inject the new child nodes into the flattened items array directly below the parent.
    • The resulting mutate event will automatically update the grid's rendering and scrollbar.

Future-Proofing

By relying on Collection.splice and the flattened _items array, this implementation provides the exact hooks needed for a future BufferedStore (which would inject placeholder rows instead of actual data rows, triggering targeted fetches as they scroll into view).

Dependencies

  • Depends on the core TreeStore implementation (#9406).
tobiu added the enhancement label on Mar 9, 2026, 12:33 PM
tobiu added the ai label on Mar 9, 2026, 12:33 PM
tobiu added the core label on Mar 9, 2026, 12:33 PM
tobiu added parent issue #9404 on Mar 9, 2026, 12:34 PM
tobiu assigned to @tobiu on Mar 9, 2026, 12:34 PM
tobiu referenced in commit bf28e6c - "feat: Add Async Subtree Loading to TreeStore (#9413) on Mar 9, 2026, 3:30 PM
tobiu
tobiu Mar 9, 2026, 3:30 PM

Input from Gemini 3.1 Pro:

✦ Implemented Async Subtree Loading for Neo.data.TreeStore.

  • Added isLoading: Boolean to Neo.data.TreeModel.
  • Upgraded TreeStore.expand() to handle asynchronous requests via load({append: true}) when expanding unloaded nodes.
  • Added state tracking (isLoading = true) and recordChange events to trigger UI spinners.
  • Hardened TreeStore.add() to ensure it only mathematically computes the visible flat list for truly new root nodes, gracefully swallowing asynchronously fetched child subtrees so expand() can splice them into the correct location without duplicating array append operations.

Changes pushed to dev.

tobiu closed this issue on Mar 9, 2026, 3:31 PM
tobiu cross-referenced by #9416 on Mar 9, 2026, 3:46 PM