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.
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>
0.41.2
I'm looking for a way to submit and/or validate the form externally or pass in errors that I have validated externally
While external submission isn't supported, the lib has support for custom validation.
Our design team wants the buttons to be above the form in a header instead of below and the structure of the application prevent wrapping the header inside the form
This is rather weird semantically speaking. I'd probably try to position the regular form children buttons using CSS, but I'm not in your shoes.
Related to #155.
@aackerman
Some ideas:
you might be able to make 2 linked forms with the same ui:rootFieldId and then use css or some other magic to hide and show the bits you need? https://github.com/mozilla-services/react-jsonschema-form#autogenerated-widget-ids
You might be able to transclude the buttons and then use callback refs to trigger click on the right buttons:
const YourForm = () => {
return (
<div>
<button onClick={() => this.submitBtn.click()}>Submit from above</button>
<Form ref={(el => this.form = el)}
schema={{foo:'bar'}}>
<button type="submit" ref={(el => this.submitBtn = el)}>Submit</button>
</Form>
</div>
)
};
const YourForm = () => {
return (
<div>
<button onClick={() => this.form.submit()}>Submit from above</button>
<Form ref={(el => this.form = el)}
schema={{foo:'bar'}} />
</div>
)
};
Not tested.
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:
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.
Thanks for the input @lucaas. I had tried the first two and were unacceptable but I didn't get around to trying number 3.
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.
@spacebaboon sounds good. Would you be willing to create a jsfiddle and contribute it to the Tips & Tricks section?
@n1k0 sure thing :-) might be a few days till I can find time, though.
Or, could we expose the underlying DOM form element as part of the API? e.g.
constructor() {
this._assignFormRef = (formElement) => {
this.formElement = formElement;
};
}
customFormSubmissionHandler() {
this.formElement.submit();
}
render() {
<Form ref={this._assignFormRef} />
}
@n1k0 Here's the jsfiddle:
https://jsfiddle.net/spacebaboon/g5a1re63/
I hide the form's submit button with CSS, and provide a ref to it. I also use a ref to get the Form component itself, so the container component has a way to refer to the button, then just call click on it. The external control panel component receives a click handler prop that links up the two.
If you're happy with the fiddle, would you like me to add the link to the Tips and Tricks section and submit a PR?
There's also this example https://jsfiddle.net/n1k0/jt3poj2v/1/, but I like the one you suggest as it has a Form wrapper. Your call which one is better.
I guess I prefer mine, as the code is all in components, and it has a CSS border to visually illustrate that the components are separate, but yours is shorter. Up to you :)
Well I don't like mine outscopes the submit action, so yours is fine by me. Please send a PR :)
Done :)
Note: I've just learned about the ability to attach inputs external to the form with html5 https://www.impressivewebs.com/html5-form-attribute/
This might the easiest way to deal with this sort of issue.
That's really nice :smile:
Unfortunately the support from everyone's favourite browser vendor isn't quite there yet
http://caniuse.com/#search=form
Will this work for validation as well? Just calling this.validate on the underlying element from the wrapper?
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?
Is this exemple (https://jsfiddle.net/spacebaboon/g5a1re63/) still working ? I cannot import this JSONSchemaForm.default
it works for me just now in Chrome on OSX.
On Tue, 28 Aug 2018 at 08:22, Anthony notifications@github.com wrote:
Is this exemple (https://jsfiddle.net/spacebaboon/g5a1re63/) still
working ? I cannot import this JSONSchemaForm.default—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/mozilla-services/react-jsonschema-form/issues/500#issuecomment-416465259,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAqG9C4I3A1ZyPEZC7Khu39qOBgqgc-Oks5uVOG5gaJpZM4MPqrr
.
@spacebaboon
How did you import this line :
const RJSForm = JSONSchemaForm.default;
@n1k0 @spacebaboon: it works both of your ways but at the particularly @spacebaboon case I don't see how the validation can apply on it when I tried at your js fiddle example it's not helping to validate,[liveValidate ={true}],
Scenario: I'm handling submit function outside the form with a button called Next which is purposed for two functions one to submit the form for that current page and the second function is an additional progress section on the top of the form, here I'm going attach source :
**class MarsForm extends Component {
constructor(props) {
super(props);
this.state = {
current: 0,
};
}
onSubmit = ({formData}) => {
}
getSubmit = ({formData})=> {
}
handleOnChange(value) {
}
handleCancel() {
if (this.state.saveSuccess || this.isSaving.call(this)) return false;
this.props.onClose();
}
async componentDidMount() {
const { key } = this.props.match.params;
const { formData: data } = await Service.getData(key)
this.setState({ data });
}
next() {
const current = this.state.current + 1;
this.setState({current });
}
prev() {
const current = this.state.current - 1;
this.setState({ current });
}
render() {
const { current } = this.state;
return (
);
}**
@spacebaboon This is awesome. I can't seem to get it to work with one button and multiple forms. We have a tabbed display; each tab has a form. When the user clicks the main submit button in the parent component, I'd like the forms to all try and submit. Do you know a way to do that? Thanks!
@spacebaboon.
Well, If I understood correctly,
I followed the way you said like having tabbed display, well in fact not
exactly that, but I worked as multiple steps of form each step [some
portion of the form, as an array of schemas, ={schema1, schema2,
----;}]each step I made a next button available handle next button outside
the form [made default submit: hidden], added submit button reference to
next on each page and validating and sending data to collect backend.
thanks
On Wed, Dec 19, 2018 at 9:04 AM Jen Duncan notifications@github.com wrote:
@spacebaboon https://github.com/spacebaboon This is awesome. I can't
seem to get it to work with one button and multiple forms. We have a tabbed
display; each tab has a form. When the user clicks the main submit button
in the parent component, I'd like the forms to all try and submit. Do you
know a way to do that? Thanks!—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/mozilla-services/react-jsonschema-form/issues/500#issuecomment-448626739,
or mute the thread
https://github.com/notifications/unsubscribe-auth/Ai2-z3QJLjeK3KI9UqwqwsezMj83PX0yks5u6lWYgaJpZM4MPqrr
.
@praveen747 We'd like to avoid having a button on each step. We have a submit button but it lives outside the forms in the parent component. When the user clicks submit, we would like the separate forms to validate. I'm doing that by passing down a state value to componentWillReceiveProps. From there I call the ref to the form this.form.submitButton.click(); This is where the error happens.
I get the following error: An invalid form control with name='' is not focusable. I believe it is occurring b/c that particular form is not currently visible based on the tab that is being displayed. The error occurs on the tabs that are not currently active.
Anyone have any advice on how to move forward?
Is there any way to collect the errors onSubmit? I'm also submitting multiple tabs via ref.onSubmit, but I need to invoke a callback after submission of all forms, but the ref.state.errors array is always empty on submit.
@StevenVerbiest usually, onSubmit is called only when there are no errors on the form. What exactly do you mean by your question?
@StevenVerbiest we found no way to collect errors on different tabs because if the tab is not rendered, it doesn't exist in the DOM. We were getting errors "An invalid form control with name=foo is not focusable".
We ended up using on change for text boxes and populating redux state with the values. I then have a required fields json object that I run through on submit and check for values there. I throw an error in the parent component and display it on the page. Thats the best we could come up with for what we need.
@epicfaace I'm calling onSubmit programmatically, which triggers the validation, but there is no way of collecting any errors after submit on runtime. It seems the errors are set asynchronously?
@jduncanRadBlue my case is a bit different, because the tabs _are_ rendered in my component. Do you manually validate your data or do u use the jsonschema-form validator?
I'll try to set up a test case at a later time.
@StevenVerbiest how are you calling onSubmit programmatically? Can you send a code 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.
Is it possible to call submit from the external button?
I would appreciate any example!
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.
Most helpful comment
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?