react-jsonschema-form: Unable to trigger form submit/validation externally from the form

Prerequisites

  • I have read the documentation;
  • In the case of a bug report, I understand that providing a SSCCE example is tremendously useful to the maintainers.

Description

Requirements from my design team require the submit buttons to be logically separate in structure, both is usage of React and the DOM. I’m looking for a way to submit and/or validate the form externally or pass in errors that I have validated externally. It doesn’t seem possible right now and I couldn’t find a way to do it that didn’t seem like a complete hack relying on the internals of RJSF.

Steps to Reproduce

The following is essentially the crux of the structure. Our design team wants the buttons to be above the form in a header instead of below and the structure of the application prevents wrapping the header inside the form. Any help with the ability to submit/validate/display errors without having buttons as children of the form would be useful.

<div>
  <Header>
    <button>Submit</button>
    <button>Cancel</button>
  </Header>
  <Form {...formProps}>
    <span/>
  </Form>
</div>

Version

0.41.2

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 38 (13 by maintainers)

Most upvoted comments

So I just had a look through the source code, and I noticed that there is no way to call the validate function directly apart from through the onSubmit or onChange functions. Is there any way you guys would consider adding the ability to only validate the code without requiring submission? Doesn’t look it would be too much of a hassle, right? Or am I just completely underestimating the problem?

So I was looking for a nice solution here. I could not use new logic implemented here because some other UI team already wrapped react-jsonschema-form in a functional component without exposing its reference point.

So my first idea was to use native HTML5 form control reference.

<JsonForm
  id="json-form"
  onChange={(e) => {
    setForm({ ...form, fdCopy: e.formData });
  }}
  onSubmit={onSubmit}
></JsonForm>;

<Button value="Start" type="submit" form="json-form" />;

This is working fine and there is nothing wrong with this solution but I was looking further to get things done in more reactive fashion. I have noticed that react-jsonschema-form is exposing {children} prop which can be used as a form submitter. Decided to create a reference to that {children} button. Ended up with following solution:

import React, { createRef } from "react";

const submitFormRef = createRef();

<JsonForm
  onChange={(e) => {
    setForm({ ...form, fdCopy: e.formData });
  }}
  onSubmit={onSubmit}
>
  <button ref={submitFormRef} type="submit" style={{ display: "none" }} />>
</JsonForm>;

<Button value="Start" onClick={() => submitFormRef.current.click()} />;

It’s working like a charm! Attaching here as maybe someone is looking for a similar thing.

In our application we also have found the need to trigger form submission externally.

The form is presented in a dialog where the primary actions (submit) is in the dialog footer, and not part of the form which is in the dialog body. I also want to submit the form programatically when the user presses Ctrl/Cmd+Enter.

I’ve tried the following approaches to work around the problem:

  1. Calling submit on the HTMLFormElement.
  2. Create a custom submit event and dispatching it on the form.
  3. Adding a hidden input[type=submit] to the form, and calling click() on it.

Alternatives 1, 2 caused a page reload in Firefox, but alternative 3 seems to work so far.

It would be nice if this was supported by this library in the future.

We’ve done something like Lucaas’ number 3. We render our own submit button as a child of the Form with a ref, then get the element by its ref and click() it.

Thought I would throw my hat in the ring and provide a more explicit solution codesandbox

import React, { createContext, useContext, useState } from "react";
import { withTheme } from "react-jsonschema-form";

export const Context = createContext();

export default function App() {
  const [ref, setRef] = useState(null);

  return (
    <div>
      <Context.Provider value={{ setRef, ref }}>
        <FarAwayForm />
        <FarAwayButton />
      </Context.Provider>
    </div>
  );
}

function FarAwayForm() {
  return <MyForm />;
}

// The button
function FarAwayButton() {
  const { ref } = useContext(Context);
  return (
    <button type="submit" onClick={() => ref.click()}>
      Submit
    </button>
  );
}

// The form
const Form = withTheme({});

const schema = {
  title: "Test form",
  type: "string"
};

function MyForm() {
  const { setRef } = useContext(Context);
  return (
      <Form schema={schema}>
        <button ref={setRef} style={{ display: "none" }} />
      </Form>
  );
}

Is it possible to call submit from the external button? I would appreciate any example!

@StevenVerbiest I use my own validation. The jsonschema-form didn’t know what to do with fields that weren’t rendered b/c the tab info wasn’t in the dom. I kept getting the focus errors and gave up; decided to write my own.

Will this work for validation as well? Just calling this.validate on the underlying element from the wrapper?

@n1k0 sure thing 😃 might be a few days till I can find time, though.