How to get all values after submit - not touched fields included - react-final-form

I need to get all values in onSubmit method, not only dirty fields.
import React from 'react'
import { Form, Field } from 'react-final-form'
const App: React.FC = () => (
<Form onSubmit={values => console.log(values)}>
{({ form: { submit } }) => (
<>
<Field name="street" component="input" placeholder="street" />
<Field name="city" component="input" placeholder="city" />
<Field name="state" component="input" placeholder="state" />
<button onClick={submit}>Submit</button>
</>
)}
</Form>
)
export default App
Actual result:
{street: "A", city: "B"}
Expected result:
{street: "A", city: "B", state: null}

🏁 Final Form treats '' and undefined as more or less equivalent.
You'll need to provide an initialValues={{ street: null, city: null, state: null }} to get your expected result. However, if the user touches field, changes the value, and then changes it back to empty, the street key will be deleted from the form values (see above link). You could get around that by providing a parse={v => v} to cancel the normal ''-to-undefined conversion.
Hope that helps?

Related

react-hook-form and react-datetime: How to set the time to moment() from a button

I am using react-datetime inside a react-hook-form
I want the user to easily set the time to current time using a button Immediate. Instead of selecting the current time manually.
I am trying the below
const [currentDateTime, setcurrentDateTime] = useState(null);
<Controller
name="resetDateTime"
control={control}
required
render={({ field }) => (
<Datetime
onChange={setcurrentDateTime}
inputProps={{
placeholder: "MM-DD-YYYY HH:mm",
}}
value={currentDateTime}
viewMode="time"
/>
)}
/>
<Button color="primary" className="ml-1" onClick={() => setcurrentDateTime(moment())}>
{"Immediate"}
</Button>
The problem is onSubmit the react-hook-form I get resetDateTime = undefined
How to implement this properly. So I can use the Immediate button and also submit form and get the resetDateTime value
You're mixing RHF with your local state currentDateTime and are not linking the. field to RHF as you're missing to spread the field object to your <Datetime /> component.
The correct way would be to use RHF's setValue to update your resetDateTime field and get rid of the useState hook.
const { control, handleSubmit, setValue } = useForm();
<Controller
name="resetDateTime"
control={control}
required
render={({ field }) => (
<Datetime
{...field}
inputProps={{
placeholder: "MM-DD-YYYY HH:mm",
}}
viewMode="time"
/>
)}
/>
<Button color="primary" className="ml-1" onClick={() => setValue("resetDateTime", moment())}>
{"Immediate"}
</Button>

How do I access data from Form.Item?

export function URLInput() {
const [value, setValue] = useState("");
return (
<Row>
<Col span={24}>
<FloatLabel label="URL" name="url" labelValue={value}>
<Form.Item
hasFeedback
name="url"
rules={[
{
required: true,
min: 5,
type: "url",
whitespace: true,
},
]}
>
<Input
name="url"
onChange={(event) => setValue(event.target.value)}
/>
</Form.Item>
</FloatLabel>
</Col>
</Row>
);
}
I currently have this Form.Item setup.
If there is already a value in url set in the form's initialValues, the Form.Item will magically populate the input field with the value.
However, I want to be able to access this value too so that I can initialize value in
const [value, setValue] = useState("I want to initialize the value here without introducing a props");
How do I do this?
Edit:
The <URLInput> component is actually used inside of a <Form> like this:
<Form
form={form}
onFinish={handleFormSubmit}
initialValues={{ content: content}}
>
<URLInput/>
</Form>
Pass form to URLInput as a prop, then inside URLInput you can access the initialValue url by invoking the following form.getFieldValue('url').
Please see working CodeSandbox example below:
https://codesandbox.io/s/antdform-accessing-formvalues-in-child-formitem-component-uwdb7
Happy coding!

Automatic "for" and "id"

Because we cannot assume our components are singletons: I am trying to automatically handle giving htmlFor to a label and field. We see below I use useMemo and lodash uniqueId to memoize a unique id for the form on initial render. I had to give useMemo and empty array as second argument so it never re-calculates the id. Is there some automated way to handle this in final-form?
import React, { useMemo } from 'react';
import { Form, Field } from 'react-final-form';
import { uniqueId } from 'lodash';
function TaskForm() {
const id = useMemo(() => uniqueId('_form'), []);
const getFor = name => name + id;
return (
<>
<h3>Create a task</h3>
<Form onSubmit={onSubmit}>
{({ handleSubmit, pristine, invalid, ...rest }) => {(
<form onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor={getFor('firstName')}>First Name</label>
<Field name="firstName" id={getFor('firstName')} component="input" placeholder="First Name" />
</div>
<button type="submit" disabled={pristine || invalid}>Submit</button>
</form>
)}}
</Form>
</>
);
}

Unable to access mutator functions in Wizard form page while using react-final-form

I am trying to create a Wizard form using react-final-form by referring to this code https://codesandbox.io/s/km2n35kq3v. For my use case I need some mutator functions to be used inside my form fields. This example illustrates how to do that - https://codesandbox.io/s/kx8qv67nk5?from-embed.
I am not sure how to access mutator functions in my form steps when I am using a wizard form instead of a single page form.
I tried to combine both the examples by modifying the <Form> component rendered by Wizard.js to pass in the mutators. However I cannot access these mutators in the Wizard form pages.
In Wizard.js
return (
<Form
mutators={{
// potentially other mutators could be merged here
...arrayMutators,
}}
render={({
handleSubmit,
submitting,
values,
pristine,
invalid,
form: {
mutators: {push, pop, remove},
},
}) => {
return (
<form onSubmit={handleSubmit}>
Another file index.js
<Wizard
initialValues={{ employed: true, stooge: "larry" }}
onSubmit={onSubmit}
>
<Wizard.Page>
<FieldArray name="customers">
{({ fields }) =>
fields.map((name, index) => (
<div key={name}>
<label>Cust. #{index + 1}</label>
<Field
name={`${name}.firstName`}
component="input"
placeholder="First Name"
/>
<span
onClick={() => fields.remove(index)}
style={{ cursor: "pointer" }}
>
❌
</span>
</div>
))
}
</FieldArray>
</Wizard.Page>
</Wizard>
It errors out - remove is undefined in index.js
Look at this working example: https://codesandbox.io/s/znzlqvzvnx
changes I have made:
Wizard.js
static Page = ({ children, mutators }) => {
if(typeof children === 'function'){
return children(mutators);
}
return children;
};
...
<form onSubmit={handleSubmit}>
{
// activePage
<activePage.type {...activePage.props} mutators={mutators} />
}
...
index.js (only first <Wizard.page>)
<Wizard.Page>
{
({ upper }) => (
<React.Fragment>
<div>
<label>First Name</label>
<Field
name="firstName"
component="input"
...
</div>
</React.Fragment>
)
}
</Wizard.Page>

Validate on Blur

I've created a JSFiddle to help demonstrate my question: http://jsfiddle.net/jeffreyrswenson/CrYWn/5/
Here's what I'd like to see:
Messages should not appear when page loads.
Messages should appear when submit button is pushed.
Messages should appear after input value is changed and user leaves element. (Tabs or clicks to next field)
Messages should appear after user leave an input without changing.(For example a field is required and the user tabs through the field, but doesn't enter a value. I'd like the validation message to appear when this happens.)
The first four work as I'd expect. Is the last item possible and if so, what do I need to change to enable that behavior?
HTML:
<label>First name:
<input data-bind='value: firstName' />
</label>
<br/>
<label>Last name:
<input data-bind='value: lastName' />
</label>
<br/>
<button type="button" data-bind='click: submit'>Submit</button>
<br/>
<span data-bind='text: errors().length'></span> errors
ViewModel:
var viewModel = function () {
ko.validation.configure({
decorateElement: true,
registerExtenders: true,
messagesOnModified: true,
insertMessages: true,
parseInputAttributes: true,
messageTemplate: null
});
this.firstName = ko.observable().extend({
required: true
});
this.lastName = ko.observable().extend({
required: true,
pattern: {
message: 'Hey this doesnt match my pattern',
params: '^[A-Z0-9]+$'
}
});
this.submit = function () {
if (this.errors().length == 0) {
alert('Thank you.');
} else {
this.errors.showAllMessages();
}
};
this.errors = ko.validation.group(this);
};
You just need to use the standard valueUpdate option of the value binding where you can specify additional events to trigger your property change and with that the validation.
So you just need to add the valueUpdate: "blur" setting on your bindings:
<label>First name:
<input data-bind='value: firstName, valueUpdate: "blur"' />
</label>
<br/>
<label>Last name:
<input data-bind='value: lastName, valueUpdate: "blur"' />
</label>
Demo JSFiddle.
In my case, I needed the value to update after key down because I was making some fields visible if the input had a value. I wanted the underlying value to update but didn't want the validation to show until the user tabbed to the next input.
A bit of CSS and a couple of bindings is what worked for me:
CSS:
div.validationWrapper.standard-focus.has-focus .validationMessage
{
display: none;
}
HTML:
<div class="validationWrapper standard-focus" data-bind="css: { 'has-focus': MyObservableHasFocus() }">
<input class="standard-focus" type="text" data-bind="hasFocus: MyObservableHasFocus, value: MyObservable, valueUpdate: 'afterkeydown'" />
</div>
Knockout:
self.MyObservable = ko.observable('').extend({/* Your validation here */});
self.MyObservableHasFocus = ko.observable(false);
The result is an observable that updates it's value after key up and shows the validation message after it loses focus.

Resources