preserve the form data so when we update the additionalErrors the dat… - #2478
preserve the form data so when we update the additionalErrors the dat…#2478kchobantonov wants to merge 30 commits into
Conversation
…a is not reset to the initial state
✅ Deploy Preview for jsonforms-examples ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
@sdirix please review, to check the issue in the current master branch just apply the change for ExampleView.vue and then try to test the additional-errors example, e.g. change the form once it is loaded and then click on the "Add Additonal Error" button and observe that the form data is reset, then check the https://deploy-preview-2478--jsonforms-examples.netlify.app/vue-vuetify/#additional-errors |
|
So the error occurs only if the user of JSON Forms:
I don't think that this is a valid use case. Either the props of the JSON Forms component are "frozen" and the management is done by JSON Forms, or the props are always live. The error case is a weird mix which is not really valid. The same issue will occur in Angular and React if only parts of the props are updated. Either they should never be updated by the user, i.e. uncontrolled variant, or they should all be updated, i.e. controlled variant. |
|
I understand the distinction between controlled and uncontrolled usage, but in practice consumers of JSON Forms may mix these patterns—intentionally or unintentionally—especially in larger applications with multiple state sources. Even if this “weird mix” is not the recommended pattern, supporting it makes JSON Forms more fault-tolerant and easier to integrate in real-world scenarios where the state flow is not always perfectly aligned with controlled/uncontrolled paradigms. |
…the data is changed
…imple type like number, string, array and etc.
|
@sdirix @lucas-koehler please also check the update where the generated schema and uischema are properly regenerated when the data is changes - please check this example dynamic when if you change the data to a number and save then the ui will be updated accordingly - in previous versions that was not true. |
…to non null uischema in JsonForms
|
@sdirix please review |
24c8cb2 to
d86047e
Compare
|
@sdirix @lucas-koehler please review |
lucas-koehler
left a comment
There was a problem hiding this comment.
Hi @kchobantonov ,
Thanks for the updates! I have some comments inline unrelated to the setting of this.dataToUse = newEvent.data;.
I am still a bit concerned about this:
- It makes the data propagation less intuitive:
- In the controlled case, the data is set to dataToUse again after it has already been updated. Granted, this does not trigger an additional invocation of
coreDataToUpdate(). - In the uncontrolled case, this triggers
coreDataToUpdatean additional time without it being needed. This has a possible performance penalty because it invokes the middleware again - In the mixed case, it triggers the
coreDataToUpdatelike in the uncontrolled case. From a data point of view that should also not be necessary because the data is uncontrolled. Granted, this is acceptable considering the issues that this tries to fix.
- In the controlled case, the data is set to dataToUse again after it has already been updated. Granted, this does not trigger an additional invocation of
As this does make the state handling less intuitive and lowers performance in uncontrolled mode as far as I can tell, I am leaning towards not introducing supporting this mixed case.
Do you have a concrete use case in mind where this is necessary? For a diffuse robustness increase, I prefer to not introduce this because consumers can always control the data in the component rendering the form. Even if the state handling is more complex for other properties.
We can of course add this to the documentation though.
Yes I have a specific case in my camunda-jsonforms-plugin project where when the form is submitted then if the backend determines that some of the fields is not valid because of some runtime constraints then I'm returning back to the UI additionalErrors so those can be rendered on the UI to represent the server side validated fields but when I set the additionalErrors that triggers the setting of the forms data and etc. which defacto reverts my changed data to whatever I had on the first load which is definitely not what I wanted since that will not display the data in the form as it was before submitting that. It is given that I can by pass that (in the example project even I do capture the data and preserve that) but since that looks like a bug to me to be honest I wanted to see that in the core itself instead of me handling that in multiple places like the vuetify3 demo app in the jsonforms project, in my camunda-jsonforms-plugin in the jsonforms-vuetify-webcomponent project and so on. |
|
@lucas-koehler thanks for the review - please check my comments |
|
I think we just have a clash of expectations here. @kchobantonov is treating This is additionally complicated by the fact that I think it would be good to clearly differentiate the two behaviors and therefore expectations. For this we can introduce two new props, Alternatively we could also not create a new Does this make sense for everybody? @lucas-koehler What do you think? @kchobantonov would you be willing to implement this? |
|
@sdirix Thanks for the explanation. I agree with your sentiment. Both suggested solutions are fine with me but I prefer the second one to keep the API of the JSON Forms component concise. I.e.:
|
|
so what is the goal here then ? introduce another event for changing data only
So what we want is to introduce another event besides 'change' to support v-model:data ? |
sdirix
left a comment
There was a problem hiding this comment.
Yes, I think we need to cleanly differentiate between "controlled" data and "uncontrolled" data. This becomes even more clear now with the schema regeneration.
Alternative for the schema generation: We could also think about NOT handling the schema regeneration within JSON Forms and expecting the caller of JSON Forms to remount us, e.g. via a key, so we don't need to handle that case within JSON Forms. However I would be fine to keep it within JSON Forms but only for the "controlled" mode, so that we don't regenerate on simple user edits.
There are now checks for schema-less usage: when the data prop changes, we compare the newly generated schema with the current one and only update the schema/UI schema if the data structure actually changed. |
|
@sdirix Do we still plan to merge this PR, or have we decided not to pursue this approach? I'm asking because, besides the additionalErrors issue with form data, this PR also includes a fix for schema generation. Instead of:
it uses:
which is more correct, as it allows the generator to properly detect whether the input is a simple type, an array, or an object and generate the appropriate schema. If we're no longer considering merging this PR, I can create a separate PR containing just the schema generation fix and then close this one. |
|
I will re-review this PR today. Let's see. |
@kchobantonov I re-reviewed everything today, including testing the behavior in the Vuetify dev app. Most of it is in good shape and I would recommend to merge it.
The one part we don't want to merge as is: Concretely that means: keep the data prop semantics as they are and add the If you'd rather land the schema generation fix quickly, splitting it into its own PR is fine with us and should merge fast. But if you're up for the |
|
@sdirix all reported issues are now addressed - please review. |
EclipseSourceAI
left a comment
There was a problem hiding this comment.
Note
Autonomous AI review.
This review was done by an AI agent and therefore may contain mistakes. Feel free to ignore any comment you disagree with. Noting why helps, since replies are read and taken into account in follow-up reviews.
Resolving all AI comments does not lead to an automatic approval. A maintainer still needs to review and sign off on the overall architecture and design.
To get an updated review after pushing changes, a maintainer may re-request a review from this account.
Running in Eclipse Enclave, submitted via review-guard-mcp
Fixes the additionalErrors resetting form data in the Vue JsonForms component by adding an update:data event for v-model:data support, plus schema/uischema regeneration when the data prop changes externally in schema-less mode. Also carries two @jsonforms/core generator fixes: generateJsonSchema now handles non-object roots (primitives, arrays, undefined, null) via gen.property, and generateDefaultUISchema no longer returns null.
All previous review points from sdirix and lucas-koehler look addressed: the dataToUse sync is gone, the uischema fallback moved into the generator so React and Angular benefit, echo detection compares against jsonforms.core.data, and the generator tests cover the new cases.
Verified locally: build and lint clean, all package tests pass (the angular-material failure in my first run was a missing CHROME_BIN, not the PR). I also drove the Vuetify dev app: the additional-errors example keeps edited data across repeated "Add additional error" clicks, #dynamic regenerates from Name to Id on external data change, and root-level primitive data (42, "hello") renders a single working control with no console errors.
Where to focus: the new data watcher in packages/vue/src/components/JsonForms.vue, specifically the !this.schema guard (breaks the legal schema: false case, verified), and whether the two public core generator behavior changes should get MIGRATION.md entries under the existing 3.9 section. The rest of the inline comments are smaller cleanups.
| const isSameAsCurrentData = newData === this.jsonforms.core.data; | ||
| this.dataToUse = newData; | ||
|
|
||
| if (!this.schema && !isSameAsCurrentData) { |
There was a problem hiding this comment.
!this.schema breaks schema: false, which is a legal JSON Schema and allowed by the prop type. Init uses ?? (master), so schemaToUse starts out as false and then gets silently replaced by a generated object schema on the first data prop change. Use this.schema === undefined to stay consistent.
|
|
||
| const EMPTY: ErrorObject[] = reactive([]); | ||
|
|
||
| const getSchemaGeneratorInput = (data: any) => (data === undefined ? {} : data); |
There was a problem hiding this comment.
With generateJsonSchema(undefined) now returning {}, this local undefined -> {} mapping is dead weight and keeps Vue diverging from React, which passes data straight through (master). Same argument that moved the uischema fallback into the generator applies here: drop the helper and pass dataToUse.
| ); | ||
| }, | ||
| eventToEmit(newEvent) { | ||
| this.$emit('update:data', newEvent.data); |
There was a problem hiding this comment.
mounted() emits only change, so the initial core state never reaches a v-model:data parent. A middleware that transforms data inside Actions.init is then lost on the next unrelated prop change (additionalErrors hands dataToUse back into updateCore), which is the same bug class this PR fixes. Emit update:data in mounted() too.
| generateUISchema(jsonSchema, [], prefix, '', layoutType, rootSchema), | ||
| layoutType | ||
| ); | ||
| ) ?? createLayout(layoutType); |
There was a problem hiding this comment.
generateDefaultUISchema returning an empty layout instead of null is a public API behavior change and deserves a MIGRATION.md entry under the existing 3.9 section (master). It also makes the foundUISchema !== null branches unreachable in both CombinatorProperties implementations (react, vue), which could be cleaned up here.
| const gen = new Gen(findOption); | ||
|
|
||
| return gen.schemaObject(instance); | ||
| return gen.property(instance); |
There was a problem hiding this comment.
Good fix, but it changes results for existing callers: generateJsonSchema([1, 2]) used to return an object schema with "0"/"1" properties and now returns {type: 'array', items: {type: 'integer'}}. Worth a MIGRATION.md entry alongside the uischema change.
| methods: { | ||
| onChange(event: JsonFormsChangeEvent) { | ||
| this.data = event.data; | ||
| console.log(event); |
There was a problem hiding this comment.
console.log(event) doesn't show a reader anything. Either drop the handler from the sample or use it for something real (e.g. storing event.errors). The data prop docs above also still don't say it is controlled and must be kept in sync, which is what makes v-model:data the recommended binding.
…a is not reset to the initial state