I've created this codesandbox - https://codesandbox.io/s/pk8p4lvl90
I can implement the material-ui instructions (https://redux-form.com/7.2.2/examples/material-ui/) fine without the mapping mechanism, but as soon as I apply the mapping I can't get the material-ui to implement the look for a textfield.
In my example I have commented out the code I have tried that works without mapping being involved.
Form -
<form onSubmit={handleSubmit}>
<div>
{/* <Field
name="firstName"
component={renderTextField}
label="First Name"
/>*/}
<FieldArray
name="firstName"
component={renderTextField}
label="First Name"
/>
</div>
</form>
TextField Render -
const renderTextField = ({ fields, input, label }) => (
<div>
{fields.map((newIntel, index) => (
{/* <TextField
name={newIntel}
key={index}
label={label}
placeholder={label}
component="input"
placeholder={label}
label={label} /> */}
<Field
name={newIntel}
key={index}
label={label}
placeholder={label}
component="input"
placeholder={label}
label={label}
/>
))}
<div
variant="fab"
color="primary"
className="jr-fab-btn"
aria-label="add"
onClick={() => fields.push()}
>
Click me
</div>
</div>
);
In order to use redux-form features with material-ui look, you need use redux-form's input component with render function that will return material-ui's component with appropriate props. You started doing so, but renderTextField should look a little bit differently, e.g.:
const renderTextField = ({
input,
label,
meta: { touched, error },
...custom
}) => (
<TextField
hintText={label}
floatingLabelText={label}
errorText={touched && error}
{...input}
{...custom}
/>
)
Having this, you can reuse it in, let's say renderForm function:
const renderForm = ({ fields, input, label }) => (
<div>
{fields.map((newIntel, index) => (
<Field
...
component={renderTextField}
...
/>
))}
...
</div>
);
That's based on what I've found in the redux-form Docs you linked. Take another look there as well, it's well described there as well.
Related
I am trying to make my form accessible.
Here is my sandbox link: https://codesandbox.io/s/typescript-material-ui-textfield-forked-0xh13?file=/src/App.tsx
My requirements are the following:
Upon submitting a form with validation errors, the 1st input with an error should be focused
Exactly like this form: https://a11y-guidelines.orange.com/en/web/components-examples/forms/
Is there an option in material ui to achieve this?
This is my code:
import React from "react";
import TextField from "#mui/material/TextField";
import { Form, Field } from "react-final-form";
const required = (value: string) =>
value ? undefined : "This field cannot be blank";
const App = () => (
<Form
onSubmit={(form_data) => console.log(form_data)}
render={({ handleSubmit, submitting }) => (
<form onSubmit={handleSubmit}>
<Field
name="line1"
validate={required}
render={({ input, meta }) => (
<TextField
{...input}
placeholder="Required"
label="Required"
helperText={meta.error}
error={meta.touched && meta.error}
/>
)}
/>
<Field
name="line2"
render={({ input, meta }) => (
<TextField
{...input}
placeholder="Optional"
label="Optional"
helperText={meta.error}
error={meta.touched && meta.error}
/>
)}
/>
<button onClick={handleSubmit}>Save</button>
</form>
)}
/>
);
export default App;
I ended up using a plugin for react-final-form called final-form-focus. I don't think there's anything for MUI
https://codesandbox.io/s/typescript-material-ui-textfield-forked-vep9e?file=/src/App.tsx
How to hide error on onBlur? I have an error validation before submitting, but I want to hide the error on onBlue. This is how I use form. I tried to change meta, but no results. Thank you.
<Form
initialValues={{ search }}
onSubmit={onSubmit}
render={({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<Field name="search" validate={composeValidators(required, minLength)}>
{({ input, meta }) => (
<div>
<SearchInputDumb {...input} submitSearch={handleSubmit}/>
{meta.error && meta.touched && (
<SearchFieldFooter>
<SearchStyledTipIcon />
<span>{meta.error}</span>
</SearchFieldFooter>
)}
</div>
)}
</Field>
</form>
)}
/>
You could have a isInputFocused state and make the error hide with that. In your SearchInputDumb component you could implement a onFocus/onBlur methods to change the isInoputFocused state
I am trying to create a "factory" of sorts so:
This works:
<>
<span className={classes.p}>{"test"}</span>
<input ref={register} className={classes.input} name="legal_name_1" />
</>
But when I wrap the input in a function in a component it does not work:
const MyInput = ({ name, register, ...props }) => {
return (
<>
<span className={classes.p}>{AllDisasterAppFields[name]}</span>
<input ref={register} className={classes.input} name={name} {...props} />
</>
);
};
<MyInput register={register} name= name="legal_name_1" {...props} />
Any ideas?
I understand that you want to pass register as a prop.
If I'm right so You have to write your code like this (Don't use ref property but this {...register(name)}):
const MyInput = ({ name, register, ...props }) => {
return (
<>
<span className={classes.p}>{AllDisasterAppFields[name]}</span>
<input {...register(name)} className={classes.input} name={name} {...props} />
</>
);
};
<MyInput register={register} name="legal_name_1" {...props} />
How to make the react-datepicker bind with redux-form?
I have this redux-form field:
<Field name="due_date" component={props =>
<DatePicker
{...props}
readOnly={true}
selected={this.state.due_date}
value={this.state.due_date}
onChange={this.handleDueDate}
/>
} />
Whenever i submit the redux form does return an empty object not binding the datepicker's value...
my onChange :
handleDueDate(date) {
this.setState({
due_date: moment(date).format('L')
}, () => {
// do I need to dispatch and action here to update the redux-form in state tree?
})
}
you have to make a custom component to validate and use datepicker with redux-form
Try this
const datePicker = ({ input, label, type, className, selected, meta: { touched, error } }) => (
<div>
<div>
<DatePicker {...input}
selected={selected} placeholder={label}
type={type} className={className}
peekNextMonth
showMonthDropdown
showYearDropdown
dropdownMode="select"
/>
{touched && error && <span className="error_field">{error}</span>}
</div>
</div>
)
and then pass props to that custom component
<Field
name="date_of_birth"
component={datePicker}
type="text"
selected={this.state.date_of_birth}
onChange={this.handleChange.bind(this)}
className="form-control"
/>
For future readers, just go to this link: DatePicker in Redux Form
first, I created the component like metioned in link above and just passed the selected prop in it. in my case:
<Field
name="due_date"
component={DatePickerComponent}
selected={this.state.date_of_briefing} />
I'm currently rendering input controls like so:
renderField = function({ input, label, name, type, meta: { touched, error, warning } }) {
return (
<FormGroup>
<FormControl {...input} type={type} placeholder={label} />
{touched && error && <span className="error-text">{error}</span>}
</FormGroup>
);
}
render() {
const { handleSubmit, pristine, error } = this.props;
return (
<form onSubmit={handleSubmit(this.onSubmit)} className="profile-form">
<Field name="first_name" component={this.renderField} type="text" label="First Name" />
<Field name="last_name" component={this.renderField} type="text" label="Last Name" />
<Field name="bio" component={this.renderField} type="text" label="Bio" />
...
<Button bsStyle="primary" type="submit" disabled={pristine}>Save Changes</Button>
</form>
);
}
I want to change the bio field to be a textarea control instead of an input. Is it possible to do this within my renderField function. I'd like to do it there instead of having to replicate for another function such as renderTextArea since that would duplicate a lot of the arguments and bootstrap markup.
I'm not seeing any examples of this in the docs or searches but maybe I'm thinking about it wrong.
thanks to #elmeister for the comment to lead in the right direction. I was missing the componentClass prop, so on the bio field I just needed to change to
<Field name="bio" component={this.renderField} type="text" label="Bio" componentClass="textarea" />
and then in my renderField method I needed to add componentClass as an argument and add that prop to the FormControl component. I didn't need to change input to field btw, i think componentClass just overrides it when passed in.
renderField = ({ input, label, name, type, componentClass, meta: { touched, error, warning } }) => {
return (
<FormGroup>
<ControlLabel>{label}</ControlLabel>
<FormControl {...input} componentClass={componentClass} type={type} placeholder={label} />
{touched && error && <span className="error-text">{error}</span>}
</FormGroup>
);
}
You could also use Control with FormControl straightaway.
export const InputFieldComponent = ({
id,
type,
label,
fieldObject,
placeHolder,
maxLength,
srOnly,
messages, validators, onChange}: Props) => (
<FormGroup controlId={id} validationState={fieldObject.valid ? 'success' : 'error'>
<ControlLabel srOnly={srOnly}>{label}</ControlLabel>
<Control
model={`.${id}`}
type={type}
placeHolder={ placeHolder || label }
component={FormControl}
maxLength={maxLength}
validators={validators}
onChange={onChange}
>
<FormControl.Feedback> <FaCheck /> </FormControl.Feedback>
<Errors
show="touched"
model={`.${id}`}
wrapper={ (v) => <HelpBlock>{v}</HelpBlock> }
messages={messages}
/>
</FormGroup>
);
InputFieldComponent.defaultProps = {
messages: {},
srOnly: false,
type: 'text',
maxLength: 255
}
export default InputFieldComponent;