Skip to content

MudTable: opt-in AutoReloadOnCollectionChanged / AutoReloadOnItemPropertyChanged; package as ModelingEvolution.MudBlazor - #1

Merged
rafalmaciag merged 7 commits into
masterfrom
feature/table-observable-items
Aug 25, 2026
Merged

MudTable: opt-in AutoReloadOnCollectionChanged / AutoReloadOnItemPropertyChanged; package as ModelingEvolution.MudBlazor#1
rafalmaciag merged 7 commits into
masterfrom
feature/table-observable-items

Conversation

@rafalmaciag

@rafalmaciag rafalmaciag commented Aug 24, 2026

Copy link
Copy Markdown

Summary

  • MudTable<T> gains two opt-in parameters (both default false, upstream behaviour unchanged):
    • AutoReloadOnCollectionChanged — table re-renders when Items raises INotifyCollectionChanged.CollectionChanged. Subscription follows Items reassignment / flag changes, released on Dispose; notifications may arrive on any thread; a synchronous burst coalesces into one render on the next dispatcher turn. No effect with ServerData.
    • AutoReloadOnItemPropertyChangedrow-scoped: each rendered row is wrapped in an internal MudTableObservedRow<T> (keyed on the item) that observes its own item's INotifyPropertyChanged for exactly as long as the row exists and re-renders that row only. Blazor's row lifecycle is the bookkeeping — no per-table set of subscribed items, nothing to reconcile on Reset/Clear, and only visible rows (current page / virtualized window) are observed, so cost is bounded by what's on screen, not by the collection.
  • MudTableObservedRow<T> is internal (not part of the public API) and rendered from code, since Razor tag syntax only discovers public component types.
  • Parity with MudDataGrid (upstream MudDataGrid : Refresh on collection changed MudBlazor/MudBlazor#11822) for the collection half, minus its gaps (opt-in, coalesced, unsubscribed on swap/dispose).
  • Packaging: PackageId=ModelingEvolution.MudBlazor; assembly, namespace and _content/MudBlazor path unchanged → drop-in swap of one PackageReference. Tag-triggered publish on me/X.Y.Z[.N] (publish-me-nuget.yml); upstream v* workflow untouched.
  • Transitive upstream MudBlazor is superseded inside the package (buildTransitive/ModelingEvolution.MudBlazor.targets, 9.7.0.4): a consumer that also receives upstream MudBlazor — typically via MudBlazor.Markdown or Extensions.MudBlazor.StaticInput — would otherwise get two MudBlazor.dll and "Conflicting assets with the same target path _content/MudBlazor/MudBlazor.min.css". The targets drop the upstream package's compile/runtime/static-web assets in every project that references the fork directly or transitively; no per-consumer override needed. Opt out: ModelingEvolutionMudBlazorSupersedesUpstream=false.

Epic: docs/epics/epic-084-mudblazor-observable/README.md (§1c).

Commits

  1. 765f167 first version (table-level item tracking with a HashSet) — superseded by 4
  2. cdb1ae8 packaging + publish workflow (fork-only)
  3. d749d60 base-path fix — StaticWebAssetBasePath=_content/MudBlazor (9.7.0.1 served assets at site root)
  4. 2b79bb4 row-scoped AutoReloadOnItemPropertyChanged — the design to review; for an upstream PR squash 1+4
  5. d04388d UTF-8 BOM (CI CHARSET check), b752c4b cherry-pick of upstream Tests: Guard null picker reference in DateRangePickerMinMaxDaysTest MudBlazor/MudBlazor#13614 (pre-existing CS8602 on the runner's SDK)
  6. 2646ec7 buildTransitive supersede of upstream MudBlazor (fork-only, not for upstream)

Test plan

  • TableAutoReloadTests (12): default-off = no subscription & no re-render; add/remove/clear re-render; item change re-renders only that row (sibling MudTr render counts unchanged); rows observe only while rendered and follow add/remove; Clear() (Reset, no OldItems) still releases every subscription via row disposal; 500-add and 500-property-change bursts → <10 renders; Items swap moves subscriptions; flags off → unsubscribed; dispose → unsubscribed, late notifications ignored
  • Full Table suite 148/148, auto-reload tests 3× stable
  • Release build incl. bun web assets; packed .props shows BasePath=_content/MudBlazor
  • Fresh web host referencing MudBlazor.Markdown 9.0.0 + the fork, no override: builds, deps.json lists only the fork, one MudBlazor.dll, assets served from the fork
  • Published 9.7.0.4; consumed by Observable/Source/ModelingEvolution.Observable.MudBlazorSample — a background thread folds add/insert/remove/in-place updates every 50 ms into ModelingEvolution.Observable.ObservableCollection while MudTable renders it; headless Chrome confirms rows and cells update with no interaction, no console/HTTP/server errors

🤖 Generated with Claude Code

Rafal Maciag and others added 7 commits August 24, 2026 18:54
…ertyChanged

Parity with MudDataGrid (MudBlazor#11822) for MudTable: when Items implements
INotifyCollectionChanged, or items implement INotifyPropertyChanged, the table
re-renders on change without reassigning Items or calling StateHasChanged.

Both parameters default to false, so existing behaviour is unchanged.
Subscriptions follow Items reassignment and parameter changes, are released on
Dispose, and a burst of notifications is coalesced into one render on the next
dispatcher turn. Notifications may arrive on any thread. No effect with ServerData.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…pace, _content/MudBlazor path)

Tag-triggered publish on me/X.Y.Z[.N] to nuget.modelingevolution.com and nuget.org;
upstream's v* workflow is untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… at site root)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…MudTableObservedRow<T>)

Each rendered row observes its own item for exactly as long as the row exists
(rows are keyed on the item); a change re-renders that row only. Blazor's row
lifecycle is the bookkeeping: no per-table set of subscribed items, nothing to
reconcile on Reset, and only visible rows (page / virtualized window) are
observed. The table keeps only the INotifyCollectionChanged subscription.

MudTableObservedRow<T> is internal and rendered from code (Razor tag syntax
only discovers public component types).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A consumer that also receives the upstream MudBlazor package — typically
transitively via MudBlazor.Markdown or Extensions.MudBlazor.StaticInput — got
two MudBlazor.dll and 'Conflicting assets with the same target path
_content/MudBlazor/MudBlazor.min.css'. The packed buildTransitive targets now
drop the upstream package's compile/runtime/static-web assets in any project
that references ModelingEvolution.MudBlazor directly or transitively, so no
per-consumer override is needed. Opt out: ModelingEvolutionMudBlazorSupersedesUpstream=false.

Verified with a fresh web host referencing MudBlazor.Markdown 9.0.0 and no
override: builds, deps.json lists only the fork, one MudBlazor.dll, assets
served from the fork.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@rafalmaciag
rafalmaciag merged commit 6c99e35 into master Aug 25, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants