form-data-json icon indicating copy to clipboard operation
form-data-json copied to clipboard

Implement `beforeSet` callback for input elements

Open KES777 opened this issue 10 months ago • 7 comments

Is your feature request related to a problem? Please describe. I am using component like TomSelect. I leverage Ajax loading. There is no way to precreate select with all available options. Thus HTML has this to be filled does not have options, thus FormDataJson.FromJson( ... ) do nothing with it, because it does not have corresponding option.

Describe the solution you'd like Allow to configure FormDataJson to create

KES777 avatar Jan 08 '25 23:01 KES777

Hi. Thanks for the feedback. But, that's not a thing for this library. This is a typical use case for a custom select. This custom select is responsible to set selected options, when they are available. Because the app know when it's loaded and select options are available.

It should be fairly easy to recall FormDataJson on this custom select to set correct options after they are loaded.

This library does and will not support modifying select option as it has it's own big set of challanges that comes with it. Alone the fact that FormData values contains just values, not labels for select options... And so on...

brainfoolong avatar Jan 09 '25 05:01 brainfoolong

Not sure what would be the best way then... Let me provide more details what is going on.

FormDataJson allows to set data to forms. I use hierarchical feature. Data could be 2-5 or more levels deep. This is not restricted. When data is loaded, the HTML structure is created:

{
  Company: [{
    Country: { id: 17, name: 'Ukraine' }
    Email: []
    Phones: []
    Person: {
      Address: {}
      Email: []
      more deeper...
    }
  }]
}

The HTML subforms are created from block. There is no any individual inputs. After this FormDataJson comes into play. FormDataJson.FromJson( ... ) This is very convenient to set this bunch of inputs in one go. And it looks like nightmare to find individual <select> and additionally create options for them manually before FromDataJson.

But it looks very easy to implement and straightforward if FromDataJson will provide a callback just right before setting up values into input, eg. { beforeSet: function( element, type, value, data ) { ... } }. Here: element is the reference to HTML input; type is element type like 'select', 'radio', 'input'; value the value to setup 17, data the reference to the current data structure { id: 17, name: 'Ukraine' }. type probably excess, but it is already detected at FormDataJson. No need to force people to redetect HTML element again at theirs code.

In this case I could easily expand/clear select's options. I could try to implement this feature. Does it sound good?

Or do you see how it could be done simpler?

KES777 avatar Jan 09 '25 14:01 KES777

I understand the point you are asking for your special use case. The issue is not the technical part. It is just nothing what this library should do. It's not supposed to be used to manipulate the DOM. It's a Form data to<->json library. It's not a form generation library. Manipulating DOM options is kind of form generation/manipulation.

Beside the manipulation, i see no real benefit of having a callback "beforeSet" for any other valid use case.

Your use case it is really a thing that your custom select code with the ajax/async part must handle. Offloading this things to the code where form values are being set is just wrong in case of good coding practice. The form must be ready before values are set. Nobody expect that setting form values changing the whole set of available options, as no library does this. That's usually a thing in the state of "load available options/render options/init select" rather then setting selected options.

Sorry, you can for sure fork this library and modify it for your needs, that's why open source exist. But i cannot integrate it into this library. It's just not a feature that this lib should have.

brainfoolong avatar Jan 09 '25 14:01 brainfoolong

Yes, I understand that this library task is from/to json.

I would agree with you that form must be ready before values are set. But

  1. This HTML is not rendered by backend (except once the first time).
  2. Then this is raw HTML cached somewhere by nginx and/or other proxy and reused for different values.
  3. So this form can not be ready before values are set.
  4. Values comes later via WebSocket.

If I would have a chance to precreate <option>s then there is no any reason to set values separately.

Please take into account that this is not the general <select> tag with the small static list of <option>s. This is autolookup/autocomplete component. And for this component there is now any chances to precreate its options. The value/label pair comes together and should be set together.

Lets looks deeper how this autolookup/autocomplete component works. During users input this component creates more options on the fly and nobody blames developers about that. This is expected that this type of component changing the whole set of available options.

Because of dynamic nature of labels for options for autocomplete component: the labels arrive with values. So it looks naturally to create the option(s) and set the value(s) together, at the same time.

Just try to look at it from a different side, from the side of autocomplete component and not the general <select> tag.

KES777 avatar Jan 09 '25 22:01 KES777

I resolved my issues with only this one simple change:

--- a/src/form-data-json.js
+++ b/src/form-data-json.js
@@ -423,6 +423,9 @@ class FormDataJson {
         if (typeof newValues[objectKey] === 'undefined') {
           continue
         }
+        if( options.beforeSet ) {
+          options.beforeSet( row, newValues );
+        }
         FormDataJson.setInputValue(row, newValues[objectKey] || null, options.triggerCha>
       }
     }

How it looks in my code:

FormDataJson.fromJson( form, data, { beforeSet: function( row, data ) {
  if( row.inputType != 'select-one' ) { return }
  
  let select =  row.input.tomselect;
  row.input.options.length =  0;
  
  const value =  select.settings.valueField;
  const label =  select.settings.labelField;
  
  const newOption = new Option( data[label], data[value] );
  newOption.selected = true;
  row.input.options.add( newOption );
  
  select.sync();
}});

PS. I see now huge of possibilities which I can perform during .fromJson call.

KES777 avatar Jan 10 '25 03:01 KES777

Sorry, i can't do that. Even if this would be a simple change, code wise. Sometimes it's hard to stay strong, but such things as this little features is was bloats software a lot over time until the point it is unmanagable.

brainfoolong avatar Jan 10 '25 05:01 brainfoolong

Yes, this is one piece of the rest of 80% :D

Actually that is sad =( but I understand your position.

Let's keep this open and see what other people say. Probably they will find other use cases we did not count.

KES777 avatar Jan 10 '25 16:01 KES777

As it seems there is no public interest for such a feature, i close this issue now.

brainfoolong avatar Oct 09 '25 07:10 brainfoolong