render textarea control with react-bootstrap and react-redux-form - reactjs

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;

Related

react final form add extra fields as props

Hello I have two forms that are different by only one field. So I am thinking of create a common form component and pass only the extra field as an optional property.
Common form:
import { Form as ReactFinalForm, Field } from "react-final-form";
import Form from 'react-bootstrap/Form';
interface CommonFormProps {
onSubmit: (values: any) => void;
extraFields?: typeof Field[];
}
export default function CommonForm(props: CommonFormProps) {
return (
<ReactFinalForm
onSubmit={values => {
props.onSubmit(values);
}}
render={({ handleSubmit, form, submitting, pristine, values}) => (
<Form onSubmit={handleSubmit}>
// other fields
{props.extraFields}
</Form>
)}
/>
);
}
Form that needs extra field
export default function FormB() {
return (
<CommonForm
extraFields={<Field name="price" validate={required} >
{({ input, meta }) => (
<Form.Group as={Form.Row} controlId="price">
<Form.Label column sm={4}>
Price
</Form.Label>
<Col sm={8}>
<Form.Control type="text" {...input} />
</Col>
</Form.Group>
)}
</Field>}
/>
);
}
I have tried to define extraFields as typeof Field[] or typeof Field. It makes no difference and error is always
Type 'Element' is not assignable to type '<FieldValue = any, RP
extends FieldRenderProps<FieldValue, T> = FieldRenderProps<FieldValue,
HTMLElement>, T extends HTMLElement = HTMLElement>(props:
FieldProps<...>) => ReactElement<...>'. Type 'Element' provides no
match for the signature '<FieldValue = any, RP extends
FieldRenderProps<FieldValue, T> = FieldRenderProps<FieldValue,
HTMLElement>, T extends HTMLElement = HTMLElement>(props:
FieldProps<...>): ReactElement<...>'.ts(2322) index.tsx(16, 5): The
expected type comes fro
Could anybody help get rid of this please?
Pass as a child, not a prop:
export default function FormB() {
return (
<CommonForm>
<Field name="price" validate={required} >
{({ input, meta }) => (
<Form.Group as={Form.Row} controlId="price">
<Form.Label column sm={4}>
Price
</Form.Label>
<Col sm={8}>
<Form.Control type="text" {...input} />
</Col>
</Form.Group>
)}
</Field>}
</CommonForm>);
}
Then use children
export default function CommonForm(props: CommonFormProps) {
return (
<ReactFinalForm
onSubmit={values => {
props.onSubmit(values);
}}
render={({ handleSubmit, form, submitting, pristine, values}) => (
<Form onSubmit={handleSubmit}>
// other fields
{props.children}
</Form>
)}
/>
);
}
children can be empty

Other validation message is triggering instead of my message when using Ant design getFieldDecorator

I am using Ant design and this is my form when I click on save I am getting this type of validation msg instead of red bordered Antd validation msg
I want validation error like this which is shown in AntD documents.
https://codesandbox.io/s/do52z
I have writter my function like this
<Form id="myForm" onSubmit={this.handleSubmit}>
<Form.Item label="Code">
<CustomInput
form={this.props.form}
type="text"
disabled={this.state.disableFields}
name="code"
id="code"
placeholder=""
required={true}
errorMsg={"Please input code!"}
/>
</Form.Item>
</Form>
This is my custom Input
const CustomInput = ({
form, id, name, placeholder, required, errorMsg, type, disabled,}: Props) => {
return form.getFieldDecorator(id, {
rules: [
{
required: required,
message: errorMsg
}
]
})(
<Input
type={type}
name={name}
id={id}
disabled={disabled}
placeholder={placeholder}
className={name === "code" ? "code-input" : "input-box"}
/>
);
};
export default CustomInput;
and this is my save button
<Button
className="save-btn"
htmlType="submit"
form="myForm"
>
Save
</Button>
I think I am missing something little here. Thanks in advance
Ant design input doesn't have required prop..
Required prop should be give inside form.item rules prop.
Since you have given reqired to input tag it will cause Chrome to display a prompt that the user to fill out the field.
Update based on comment
Move formitem tag inside custominput component and try again.
<Form id="myForm" onSubmit={this.handleSubmit}>
<CustomInput
form={this.props.form}
type="text"
disabled={this.state.disableFields}
name="code"
id="code"
placeholder=""
required={true}
errorMsg={"Please input code!"}
/>
</Form>
..
const CustomInput = ({
form, id, name, placeholder, required, errorMsg, type, disabled,}: Props) => {
return(
<Form.Item label="Code">
{form.getFieldDecorator(id, {
rules: [
{
required: required,
message: errorMsg
}
]
})(
<Input
type={type}
name={name}
id={id}
disabled={disabled}
placeholder={placeholder}
className={name === "code" ? "code-input" : "input-box"}
/>
)}
</Form.Item>
)
};

changing an uncontrolled input of type file in React

i created redux-form to upload form-data with file. but i stumped in how handle the file input, when i tried to select file from form browser console it throw this error
component is changing an uncontrolled input of type file to be controlled. Input elements should not switch from uncontrolled to controlled.......
fileupload.js
import React, { Component } from "react";
import { Field, reduxForm } from "redux-form";
import Card from "#material-ui/core/Card";
class RegisterShop extends Component {
state = {};
renderField(field) {
return (
<div>
<label>{field.label}</label>
<input className="form-control" type={field.type} {...field.input} />
</div>
);
}
onSubmit(values) {
let formData = new FormData();
////
}
render() {
const { handleSubmit } = this.props;
return (
<div>
<Card>
<h1>Register Shop</h1>
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<Field
label="Shop Name"
name="shopname"
type="text"
component={this.renderField}
/>
<Field
label="Describe about your shop"
name="description"
type="text"
component={this.renderField}
/>
<Field
label="Email"
name="email"
type="text"
component={this.renderField}
/>
<Field
label="Contact No"
name="mobileno"
type="text"
component={this.renderField}
/>
<Field
label="Location"
name="locatiion"
type="text"
component={this.renderField}
/>
<Field
label="Shop Logo"
name="image"
type="file"
component="----------" //I changed this many ways still get same error
/>
<button type="submit" className="btn btn-primary">
Login
</button>
</form>
<br />
<br />
</Card>
</div>
);
}
}
export default reduxForm({
form: "registershop"
})(RegisterShop);
I think the problem is here.
<input className="form-control" type={field.type} {...field.input} />
First time, field.input is undefined, so fields is blank object , and input field will like this, which is "an uncontrolled input".
<input>undefined</input>
And after type something in field, the field.input will have value,so be controlled component.
And maybe change to this:
<Field
label="Shop Logo"
name="image"
type="file"
component={MyFile}
dataAllowedFileExtensions="jpg png bmp"></Field>
/>
MyFile component:
class MyFile extends Component {
render() {
const { input,dataAllowedFileExtensions } = this.props
const onInputChange = (e) => {
e.preventDefault();
const files = [...e.target.files];
input.onChange(files);
};
return (
<div>
<input type="file"
onChange={onInputChange}
data-allowed-file-extensions={dataAllowedFileExtensions} />
</div>
)
}
}
Because it’s uncontrolled component, you probably want to leave it as:
<input type=“file”>
Then figure out how to process the input.
From:
React docs
In React, an input type="file" is always an uncontrolled component because its value can only be set by a user, and not programmatically.
You should use the File API to interact with the files. The following example shows how to create a ref to the DOM node to access file(s) in a submit handler:
<input type="file" ref={this.fileInput} />

Material-UI and Redux-form not styling when being mapped

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.

Formik form not updating fields upon edit

I've been trying to rewrite my beginner form in React to use Formik.
I've gotten to the state that the form is being rendered, however, for some reason, I can't update the fields. It's clear that I made a mistake somewhere that prevents Formik from updating the state. What am I missing?
An example form component:
export const TextBox: React.SFC<FieldProps<any> & CustomFormElementProps> = ({
field, // { name, value, onChange, onBlur }
form: { touched, errors },
loading,
...props
}) => (
<div className="row form-group" key={field.name}>
<label className="col-sm-2 control-label">
<ReactPlaceholder showLoadingAnimation ready={!loading} type="text" rows={1} className="control-label">
{props.label}
</ReactPlaceholder>
</label>
<div className="col-sm-10">
<ReactPlaceholder showLoadingAnimation ready={!loading} type="text" rows={1} className="form-control">
<input type="text"
disabled={props.disabled}
className="form-control"
id={field.name}
onChange={field.onChange}
onBlur={field.onBlur} {...props} />
{touched[field.name] && errors[field.name] && <span className="text-danger">{errors[field.name]}</span>}
</ReactPlaceholder>
</div>
</div>
);
The form is initialized in another component (which acts as a page template for the website);
renderFormElements() {
var formFields = this.props.detailsElements.map((item) => {
switch (item.type) {
case FormElementType.TextLine:
return <TextLine
name={item.name}
label={item.label}
disabled={!this.state.editMode}
loading={item.loading}
value={item.defaultValue}
key={'TextBox_' + item.name}
/>
case FormElementType.TextBox:
return <Field
type="text"
name={item.name}
label={item.label}
component={InputElements.TextBox}
disabled={!this.state.editMode}
loading={item.loading}
value={item.defaultValue}
key={'TextBox_' + item.name}
/>
case FormElementType.DropDown:
return <Field
name={item.name}
label={item.label}
component={InputElements.DropDown}
disabled={!this.state.editMode}
loading={item.loading}
value={item.defaultValue}
options={item.options}
key={'DropDown_' + item.name}
/>
case FormElementType.RadioGroup:
return <Field
type="radio"
name={item.name}
label={item.label}
component={InputElements.RadioGroup}
disabled={!this.state.editMode}
loading={item.loading}
value={item.defaultValue}
options={item.options}
key={'RadioGroup' + item.name}
/>
}
});
var initialValues:{ [k: string]: any } = {};
this.props.detailsElements.map((item) => {
initialValues[item.name] = item.defaultValue;
})
console.log(initialValues);
var formSection =
(<Formik initialValues={initialValues} onSubmit={(values, actions) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2))
actions.setSubmitting(false)
}, 1000)
}}>
<Form key="mainForm">
{formFields}
</Form>
</Formik>)
return formSection;
I was assuming that the onChange event handler was taken care of by Formik, and that, if I didn't want to do special stuff, I did not need to provide anything to this.
What am I missing here?
Thanks!
your formFields function gets all of Formik props goodies.
it contains handleChange - use this handler as your on change.
Also, make sure the field "id" is the same as the values key.
const {
values,
touched,
errors,
dirty,
isSubmitting,
handleChange,
handleBlur,
handleSubmit,
} = this.props;
<FormControl
id="username"
required
placeholder="Enter Username"
value={values.username}
error={touched.username && errors.username}
onChange={handleChange}
onBlur={handleBlur}
/>
try putting the name attribute in the input element
name is what you got from field.name

Resources