jsonform / jsonform

Build forms from JSON Schema. Easily template-able. Compatible with Bootstrap 3 out of the box.
https://jsonform.github.io/jsonform/playground/index.html
MIT License
2.72k stars 553 forks source link

How to make a valid json with a onChange function ? #430

Closed plaroche-po closed 1 year ago

plaroche-po commented 1 year ago

Hi, I am trying to send a JSON Form in response to a http request but i have some onChange fields that, if i use them as in the playground example here, invalidates the JSON (a JSON can not contains a function. Even the playground with the original code indicates an error). I tried

{
  "schema": {
    "file_content":{
      "title":"File content",
      "type":"string"
    },
    "upload":{
      "type":"string"
    }
  },
  "form": [
    {
      "key":"file_content",
      "type":"textarea"
    },
    {
      "key":"upload",
      "type":"file",
      "accept":".txt,.md",
      "notitle":true,
      "onChange":"function (evt) { alert( 'test' ); }"
    },
    {
      "type": "submit",
      "title": "Submit"
    }
  ]
}

and the same code but with \" instead of ', but it gives me a Uncaught TypeError: node.formElement.onChange is not a function exception.

How can i use a function in onChange without invalidating the JSON ?

sdetweil commented 1 year ago

on the playground onChange, I added " in front of function, and then went to the end of the line and did delete, and then repeated end of line and repeat til all the function was on one line, then added "

and now the form passes validation

sdetweil commented 1 year ago

in my app where I need to pass form resident onxxx functions to the web page, i collapse all to a single line, and use escape for any embedded "

like this

"onChange": "(evt,node)=>{let value=$(evt.target).val();let p=$(evt.target).attr('name').split('[');let n=p[0];let i=parseInt(p[1]);$(\"[value*='\"+n+\"']\").closest('.tab-pane').find('.tab-content').find(\"[data-idx='\"+i+\"'] >div >input \").val(value).trigger('change')}"

the whole form is converted to text (from object) using JSON.stringify, BUT you have to add handlers for the conversions, because stringify/parse don't handle functions by default

this is my form parser in the browser web page

function parseData(data) {
    return JSON.parse(
      data,

      function (key, value) {
        if (typeof value === "string") {
          // only handle specified fields in jsonform
          switch (key) {
            case "onChange":
            case "onClick":
            case "onKeyUp":
              // get the function from the string
              // parens mean don't EXECUTE the function, just get its value
              value = eval("(" + value + ")");
              break;
          }
        }
        return value;
      }
    );
  }

my app uses socketio to send the all text content of the form to the web page from the server, and then the web page parses it (using function above) to an object that is then used to generate the form into the DOM like normal.

all this cause the function (onKey, OnChange...) aren't valid in JSON (as you've mentioned) and aren't executable in text form..(which is valid in JSON)... and my 'form' is generated from some other code, so it can change frequently

// config socket events
  activesocket.on("json", function (incoming_json) {
    let data = parseData(incoming_json.slice(1, -1));
.
.
.
.
      // replace any form from last connection
      $("#result").html('<form id="result-form" class="form-vertical"></form>');
      // insert the new form
      $("#result-form").jsonForm(data);

here are the two JSON utility functions on the server side to process for embedded functions in the form

//
// used by JSON.stringify
//
function tohandler(key, value) {
  if (typeof value === "function") {
    return value + ""; // implicitly `toString` it
  }
  return value;
}
//
// used by JSON.parse
//
function fromhandler(key, value) {
  if (
    value &&
    typeof value == "string" &&
    (value.startsWith("(") || value.startsWith("function(")) &&
    value.endsWith("}")
  ) {
    return eval("(" + value + ")");
  }
  return value;
}
plaroche-po commented 1 year ago

Thank you for your answer. I can't try today but i will test this method tomorrow.

plaroche-po commented 1 year ago

With some adaptations for my case, this works great ! Thank you again.