fix(render): honor every ActionBinding field, per-item visible, real element lifecycle and state paths - #1056
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
blove
force-pushed
the
blove/render-followups
branch
from
September 8, 2026 01:09
1e92f95 to
d652dfc
Compare
Contributor
…element lifecycle and state paths
Six defects the docs audit had to describe truthfully, each with a spec that
fails before the change:
- `signalStateStore` paths: `parsePointer` dropped segment 0 unconditionally,
so `set('count', 1)` replaced the whole state model. A path must now be
`''`/`'/'` (root) or start with `/`; anything else throws an Error naming
the path. Silent data loss is worse than a throw, so this applies in
production too.
- Element-scope lifecycle events fired only for elements carrying an
undeclared `lifecycle` field. `<render-element>` now emits `mounted` and
`destroyed` for every element it mounts, which is what makes `mountCount`
and `lastMountAt` on `RenderLifecycleService` mean something.
- `stateChange` hardcoded `path: '/'` and the full snapshot as `value`.
`signalStateStore()` now records its last mutation on the exported
`SignalStateStore` interface, and `RenderSpecComponent` reads it to report
the real path and value. Foreign stores keep `'/'` plus the snapshot.
- `ActionBinding.confirm`, `onSuccess`, `onError` and `preventDefault` were
accepted by the spec format and never read. All four are implemented,
mirroring `executeAction` in `@json-render/core` (including the
`'$error.message'` substitution in an `onError` set map).
- Action `params` were spread unresolved, so a `$state` expression reached
the handler as a literal object. They now go through `resolveElementProps`
with the element's repeat scope, as element props do.
- `visible` was ignored on the repeat branch. It is now evaluated per
repeated item in that item's scope, so `$item` and `$index` conditions can
hide individual rows.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…lifecycle and state paths Follows the library change. The pages the audit made describe these defects now describe the behavior that shipped: - `render/api/signal-state-store.mdx` — the silent-drop warning is replaced by the thrown error, and `lastChange()` plus the `SignalStateStore` return type are documented. - `render/guides/events.mdx` — the action-binding table lists all six fields, with sections for resolved params, confirmation, follow-ups and `preventDefault`; `stateChange` now reports a real path. - `render/guides/lifecycle.mdx` — `mountCount` counts element mounts too. - `render/guides/repeat-loops.mdx` and `render/getting-started/introduction.mdx` — `visible` is evaluated per repeated item. Also regenerates api-docs for the new exports. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
blove
force-pushed
the
blove/render-followups
branch
from
September 8, 2026 03:14
d652dfc to
061c4ca
Compare
blove
enabled auto-merge (squash)
September 8, 2026 03:14
Contributor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Six defects in
@threadplane/renderthat yesterday's audit had to write into the docs as caveats. Each one has a vitest spec that fails before the change and passes after.What changed
(a)
signalStateStoresilently dropped the first path segment.parsePointerfiltered out segment 0 unconditionally, soset('count', 1)parsed to the root pointer and replaced the entire state model with1. A path is now''/'/'(root) or must start with/; anything else throws anErrornaming the path and the required leading slash, in production as well as development — silent data loss is worse than a throw. A rejectedupdate()applies none of its entries.(b) Element-scope lifecycle events fired for almost nothing.
<render-element>gated itsmounted/destroyedemits on a truthylifecyclefield thatUIElementdoes not declare, so in practice only the two spec-scope events ever fired andmountCount/lastMountAtonRenderLifecycleServicenever moved past the spec mount. The gate is gone: every element the renderer mounts emits both events with itselementKeyandelementType.(c)
stateChangecould not name the path that changed.StateStore.subscribecarries no path, so the event hardcodedpath: '/'with the full snapshot asvalue.signalStateStore()now records its last mutation, exposed aslastChange()on a new exportedSignalStateStore extends StateStoreinterface, andRenderSpecComponentreads it when present. A store from another implementation has nolastChangeand keeps the'/'plus snapshot fallback.(d) Four
ActionBindingfields were accepted and never read.confirm,onSuccess,onErrorandpreventDefaultare all implemented, mirroringexecuteActionin@json-render/core:preventDefaultcallspreventDefault()on the emitted payload when it is a DOMEvent(or carries one underevent).confirmaskswindow.confirm(message)through the injectedDOCUMENT's default view; a falsy answer skips the handler and both follow-ups. Without a default view there is nobody to ask and the handler proceeds.onSuccess(set/action/navigate) andonError(set/action) dispatch after the handler's returned promise settles, or synchronously for a non-promise result or throw.'$error.message'in anonErrorset map is replaced by the thrown error's message. With noonError, an error propagates as before.(e) Action
paramswere spread unresolved. A$stateexpression insideparamsreached the handler as a literal{ $state: '/selected' }. They now go throughresolveElementPropswith the element's repeat scope, exactly as element props do; the emit payload still wins on key collisions.(f)
visiblewas not evaluated on the repeat branch. A repeating element mounted every item regardless of its condition. It is now evaluated once per item in that item's scope, so{ $item: 'done', eq: false }and$indexconditions hide individual rows.Specs added
libs/render/src/lib/signal-state-store.spec.ts— path validation onget/set/update, the root forms, andlastChange()(set, update, read from a subscriber, no-op writes).libs/render/src/lib/render-events.spec.ts— three-element mount emits one spec-scope plus three element-scopemountedevents, an element leaving the tree emitsdestroyed,mountCountreaches 4, andstateChangereports the real path for asignalStateStorewhile falling back to'/'for a foreign store.libs/render/src/lib/action-bindings.spec.ts— resolved params, confirm accepted/declined,preventDefaulton and off,onSuccessset/action/navigate (sync and async),onErrorset/action (sync throw and async rejection), and four repeat-visibility cases.24 of these failed before the implementation and all pass after.
Docs
The pages the audit made describe the defects now describe the shipped behavior, with the warning callouts removed:
render/api/signal-state-store.mdx(throw instead of silent drop,lastChange(),SignalStateStorereturn type),render/guides/events.mdx(full action-binding table plus sections on resolved params, confirmation, follow-ups andpreventDefault;stateChangepath; element-scope lifecycle),render/guides/lifecycle.mdx(mountCountcounts element mounts),render/guides/repeat-loops.mdxandrender/getting-started/introduction.mdx(per-itemvisible).api-docs.jsonregenerated for render and chat — chat'sliveStoretype moved toSignalStateStore.Verification
npx nx run-many -t lint,test,build --projects=render— green, 136 tests.npx nx run-many -t lint,test,build --projects=chat— green.npx vitest run --root apps/website— 1326 tests, 131 files, all passing.GROWTH_FORM_POLICY=growth_v1 npx nx build website— succeeds.nx e2egreen forcockpit-render-repeat-loops-angular,cockpit-render-state-management-angular,cockpit-render-element-rendering-angular,cockpit-render-registry-angular,cockpit-render-spec-rendering-angular,cockpit-render-computed-functions-angular,cockpit-ag-ui-json-render-angular, pluscockpit-chat-generative-ui-angularandcockpit-langgraph-durable-execution-angularas store consumers.There is no
CHANGELOG.mdunderlibs/render, so no changelog entry was added. Nocockpit-render-events-angularproject exists.🤖 Generated with Claude Code