Skip to content

Notify collection association states on membership changes - #2

Open
bemky wants to merge 1 commit into
masterfrom
fix-collection-state-dispatch
Open

Notify collection association states on membership changes#2
bemky wants to merge 1 commit into
masterfrom
fix-collection-state-dispatch

Conversation

@bemky

@bemky bemky commented Jul 31, 2026

Copy link
Copy Markdown
Owner

What

record.state('someCollection') never notified its listeners. viking mutates a collection association's target array in place (setTarget splices it and re-pushes), so the plugin handed State.set the same array reference on every change:

this.states[attribute].set(this.association(attribute).target);

State.set guards on oldValue != newValue, so with an unchanged reference the dispatch was skipped every time. A binding on a collection state only ever showed what it was first rendered with — a Comments (n) header, for example, kept its original count no matter how many comments were added or deleted.

Fix

Snapshot the array so its identity changes:

const read = () => Array.isArray(association.target) ? [...association.target] : association.target;

belongsTo is unchanged — its target is a record rather than an array, and it already changes identity on assignment, so it keeps passing straight through. That's what the Array.isArray check preserves.

One consequence worth noting: a collection state's value is now a detached copy rather than the live target, so it reflects membership as of the last afterAdd/afterRemove. Mutations that dispatch neither (e.g. a setTarget that only reorders the same members) won't refresh it — previously nothing refreshed it at all after the first render.

Test

Added a hasMany case to test/vikingTest.js asserting listeners fire and the value tracks membership. It fails on master with 0 == 1 (listener never called) and passes here.

npm test: 37 passing, 0 failing — including the existing belongsTo cases.

🤖 Generated with Claude Code

viking mutates a collection association's `target` array in place —
setTarget splices it and re-pushes — so passing `target` straight to
State.set handed it the same array reference every time. State's
`oldValue != newValue` guard then skipped the dispatch, and a binding on
a collection state only ever showed what it was first rendered with. A
"Comments (n)" header, for instance, kept its original count no matter
how many were added or removed.

Snapshots the array so identity changes and the dispatch goes through.
belongsTo still passes its record as-is, since `target` there is a record
rather than an array and already changes identity on assignment.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

1 participant