fix: proof-of-concept to support virtual fields added by hooks in buildFormState#15640
Open
brumm wants to merge 2 commits intopayloadcms:mainfrom
Open
fix: proof-of-concept to support virtual fields added by hooks in buildFormState#15640brumm wants to merge 2 commits intopayloadcms:mainfrom
buildFormState#15640brumm wants to merge 2 commits intopayloadcms:mainfrom
Conversation
… values on change
Virtual fields populated by afterRead hooks (e.g. embedded data from a
related document) return empty when a relationship field changes in the
admin panel.
Two things prevent this from working:
1. buildFormState never runs afterRead hooks. On initial document load,
Document/index.tsx fetches via payload.findByID() which runs all hooks,
then passes the result to buildFormState. But on subsequent form state
rebuilds (triggered by field changes), buildFormState constructs data
from client form state and skips hooks entirely.
2. Even after fixing (1), mergeServerFormState discards the server-computed
values. The onChange codepath dispatches MERGE_SERVER_STATE without
acceptValues, so shouldAcceptValue is false and value/initialValue are
stripped from the merge — the client never sees the hook output.
This commit:
- Exports afterRead from payload core
- Calls field-level and collection/global-level afterRead hooks in
buildFormState (guarded by isTopLevelSchema)
- Passes acceptValues: { overrideLocalChanges: false } in the onChange
merge so server-computed values are accepted for fields the user
hasn't manually modified
Tests that virtual fields populated by collection-level afterRead hooks are correctly included in buildFormState output — both when a relation is set (values populated) and when cleared (values nulled).
buildFormStatebuildFormState
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.
How come?
I am building a setup where a base collection
Personsis referenced by other collections likeEmployeesandUsers. To make editing that base data seamless, I include fields fromPersonsin a virtual group on other collections, in this exampleEmployeeslike so:Config
The data is populated and saved by hooks.
The bug: selecting a Person from the relationship dropdown re-renders the form after a request to
form-state, but does not include field values from the selected Person.See a gif
What?
Run
afterReadhooks inbuildFormStateand accept server-computed values in the onChange form state merge.Why?
Virtual fields populated by
afterReadhooks return empty when a relationship field changes in the admin panel. Two things prevent this:buildFormStatenever runsafterReadhooks. On initial load,Document/index.tsxfetches viapayload.findByID()(which runs hooks) and passes the result tobuildFormState. On subsequent rebuilds triggered by field changes, hooks are skipped entirely.mergeServerFormStatediscards the server-computed values. The onChange codepath dispatchesMERGE_SERVER_STATEwithoutacceptValues, soshouldAcceptValueis false andvalue/initialValueare stripped.How?
afterReadfrompayloadcoreafterReadhooks inbuildFormState(guarded byisTopLevelSchema)acceptValues: { overrideLocalChanges: false }in the onChange merge so server-computed values are accepted for unmodified fieldsNote on architecture
afterReaddirectly frompayloadcore into@payloadcms/ui. As far as I can tell, no other UI code currently does this. An alternative could be to expose something likepayload.runAfterReadHooks(collection, doc)on the Payload instance.