Modifiying jsonformsData after onChange

Hi everybody,
i am trying to modify the jsonformsData which will be controlled by the onChange Action.
I defined the schema and uischema properly, and those i am using to build an array.

The array items have a couple of fields which will be shown or hide regarding which kind of type i have selected in a dropdown field.

For different types i have different fields which will be shown or hidden as mentioned.

Unfortunately, when i do entries, and switch my type afterwards (so that the former entries will be go away from the UI) the entries will remain in the data. Therefore, i wrote some functions which validates the data if all the correct keys are set. In the end i want to send this corrected data back to the jsonFormsData.

I want to do this checks immediately onChange, but trying modifiying this data will result in an inifite loop :frowning:

If i am using a button to trigger this operations, everything is working as expected.

Has anybody an i idea of how to set this up?

Best,

Thorsten

[original thread by bladeyfocal]

Hi @bladeyfocal, when you hand over a new data object to JsonForms, then JsonForms will validate against that new data and send out a change event containing the new data (which is potentially changed, depending on your Ajv settings) and the determined errors.

When you build a loop in which you set a new data object whenever JsonForms emits a data change, you need to be careful to only change the data object when you actually changed the content of the data, otherwise you will end up in an endless loop as you mentioned.

For example, assuming your data is an array and you have some filtering utility method:

const modifyArrayContent = (data) => data.map(filterUnwantedAttributes);

const App = () => {
  const [data, setData] = useState(initialData);
  return <JsonForms
   onChange={ event => setData(modifyArrayContent(event.data)) } />
}

This will create an endless loop! When JSON Forms emits a data change this will always create a new array instance, no matter whether any content actually changed, and hand it over to JSON Forms. JSON Forms detects that it received a new data object, validates it and sends out a change event. This will trigger the creation of a new array instance and handing it over to JSON Forms etc. etc.

In short: You need to make sure to not create a new data instance unconditionally on every change.

e.g.

const modifyArrayWhenNecessary = (data) => {
  if(containsUnwantedAttributes(data)) {
    return data.map(filterUnwantedAttributes);
  }
  return data;
}

const App = () => {
  const [data, setData] = useState(initialData);
  return <JsonForms
  ...
  onChange={ event => setData(modifyArrayWhenNecessary(event.data)) } />
}

If you still run into an endless loop, then you probably re-add attributes via Ajv which are then filtered by your filter function again.

In general I would recommend against filtering data attributes on the fly like this, but just strip them out before you send them to the server or before handing it over to some other services/frameworks you are using.

By stripping them out immediately everything the user typed in before the enum change will be lost when they switch the enum. Maybe they just want to switch the enum to see what is the difference or by accident. When going back everything they entered is gone.

[bladeyfocal]

Hi @sdirix , thanks for the quick response, i’m not quite sure if i understand everything, as i am completely new to react,typescript and jsonfroms. In parallel to your response i found a solution that works for me, maybe this is also what you are talking about. Would be nice if you can proof that i am on the right track.

Here is my solutions as of now:

my JsonFormsTag has a on change which is calling a method updateData with the current state data object:
onChange={({ data, errors }) => updateData(data)}

i have also created a function which checks, wheter changes in the data are necessary or not which is called createNewModelthis returns a data object.

The update Data looks like the following:

const updateData = (data: any) => {
    let newmodel = createNewModel(data);

    let dataString = JSON.stringify(data);
    let modelString = JSON.stringify(newmodel);

    if(dataString == modelString) {
      setJsonformsData(data);
    }else{
      setJsonformsData(newmodel);
    }
  }

if no adjustments are necessary, the newmodel has the same content as data. So i am comparing the JSON-Strings of data and newmodel. Afterwards, if they are different i set the newmodel for jsonformsdata, otherwise data will be kept.

My only concerne here is, that if newmodel is different and i set it to jsonformsdata, this method will be run twice. If there are no changes to be made, the method runs just one time. But i think if there are changes, it is ok to run it twice to have a synced status.

What do you think?

Best Regards

Thorsten

Hi @bladeyfocal(bladeyfocal), yes in principal this is the same concept, i.e. you use the original data when there are no changes and the modified data when there are ones.

I would recommend not doing a stringify-compare but using lodash’s isEqual which should be more stable and more efficient.

[bladeyfocal]

Worked fine, thank you very much