createMaterialBidirectionalSync
projects/common/data/material-bridge/bidirectional-sync.ts
Description#
Single shared bidirectional-sync factory for cngx organisms /
directives that bridge a cngx presenter against a Material parent
(<mat-tab-group>, <mat-stepper>, etc.).
Lives at Level 2 in @cngx/common/data parallel to
createCommitController. Material types never enter the signature —
the caller maps Material-specific events and property accessors to a
host-agnostic shape at the directive boundary.
Installs:
- A presenter→Material
effect()that trackspresenterIndexand writes throughwriteSelectedIndex, equality-guarded againstreadSelectedIndex()to suppress redundant writes. The Material read+write pair runs insideuntracked()perreference_signal_architecturerule 2. - A Material→presenter subscription that forwards each
selectionChange$emission toonMaterialSelection(equality-guarded againstpresenterIndex()so a Material event whose value already matches the presenter is dropped — closes the re-entrancy loop).
Both sides clean up via destroyRef.
Material-eager-advance reconciliation. Material's MDC click
handler advances selectedIndex before the Material→presenter
subscription forwards the click. When the presenter HOLDS its index
(pessimistic mode + bound commitAction), the subscriber does NOT
write Material back: on a SUCCESSFUL commit the presenter later
advances to the clicked target and Material is already there, so the
mirror effect is a no-op (no flash); on a REJECTED commit the presenter
never moves, leaving Material eager-advanced on the refused tab. The
host detects the rejection through its own commit lifecycle and calls
CngxMaterialBidirectionalSyncHandle.reconcile to snap Material
back. Writing from inside the subscriber instead is unsafe: Material's
selectedIndex read-back lags a programmatic write, so the mirror
effect's equality guard reads a stale value and skips the corrective
write, sticking the visual on the wrong tab.
Self-echo suppression (loop-safety). Every programmatic
selectedIndex write makes Material re-emit selectionChange. The
factory records each value it writes and drops the matching echo
(indexOf + splice, which also prunes earlier writes whose echo
Material coalesced away) before any other processing. This is
load-bearing for an ASYNC commit-action: Material emits the echo a
microtask later, by which point the commit may have advanced
presenterIndex, so an equality-only guard (presenterIndex() === idx) would FAIL to drop the echo — it would re-enter the subscriber
and ping-pong against the in-flight navigation (the demo freezes).
Matching the echo by the value we wrote drops it regardless of where
the presenter has moved.
Signature#
createMaterialBidirectionalSync(opts: CngxMaterialBidirectionalSyncOptions)Parameters#
Returns#
CngxMaterialBidirectionalSyncHandle