Im trying to set a boolean value on an object to submit them all together, all the values comes from a form and most from text inputs, all text inputs are setting correctly except for my boolean element, Im not understanding exactly what Im doing wrong so any help is very appreciated. Heres my code:
import React, {useState} from 'react';
import {Col} from "react-bootstrap";
import EditorElement from "../components/editorElement";
import BootstrapSwitchButton from "bootstrap-switch-button-react";
export default function ListDetails({listdetails, updateData, updatingData, selectedRow}) {
const [input, setInput, bool] = useState(
{
enabled: listdetails.enabled, // <-- Here I set (or want to set) the value of my boolean, the "listdetails" parameter are the default values that comes from initial fetch.
name: listdetails.name,
custom1: listdetails.custom1,
id: selectedRow
}
);
const updateInputs = event => setInput({...input, [event.target.name]: event.target.value});
const updateInputsBoolean = event => setInput({...input, [event.target.name]: event.target.checked}); //<-- Here I updatethe value of my bool element
const{enabled, name, hooktype, custom1, custom2, custom3, custom4, custom5, endpoint} = input; // <-- Here I update all the values
const handelSubmit = evt => {
console.log(input);
updateData(input, bool); //<-- Here I set the collected object and set it but "bool" is not setting
};
function handleBoolean(ev) { // <-- If I run this function in the boolean onChange I get the true or false value on console but I couldnt set it in the updateData (that comes from another component where I have the axios post)
const setBool = ev.toString();
console.log(setBool);
// updateData(setBool);
}
return (
<div>
<Col className={'col-md-6'}>
<EditorElement name='Enable/ Disabled '>
<div>
<BootstrapSwitchButton
checked={enabled === true}
onstyle="primary"
offstyle="danger"
name={'enabled'}
value={enabled}
onChange={e => updateInputsBoolean(e)} // <-- Here is my boolean element that is a bootstrap switch
/>
</div>
{listdetails.enabled ? 'is enabled' : 'is not enabled'}
</EditorElement>
<EditorElement name='Name'>
<input
type={'text'}
defaultValue={name}
name={'name'}
className={'form-control'}
onChange={e => updateInputs(e)}
/>
</EditorElement>
</Col>
<Col className={'col-md-6'}>
<EditorElement name='Custom1'>
<input
type={'text'}
name={'custom1'}
defaultValue={custom1}
className={'form-control'}
onChange={e => updateInputs(e)}
/>
</EditorElement>
</Col>
<Col style={{'marginBottom': '30px', 'marginTop': '20px'}} className={'col-md-12 text-right'}>
<button
style={{'marginTop': '15px', 'marginBottom': '15px'}}
type={'button'}
className={'btn btn-primary'}
onClick={handelSubmit} //<-- Here I handle my submit
>
{updatingData ? 'Updating...' : 'Save'}
</button>
</Col>
</div>
);
}
Thanks in advance for any help!
#DrewReese helped me understand this, I was confusing BootstrapSwitchButton as a normal input and it is not like that. Here is the working code now:
const [input, setInput] = useState(
{
enabled: listdetails.enabled,
name: listdetails.name,
custom1: listdetails.custom1,
id: selectedRow
}
);
const updateInputs = event => setInput({...input, [event.target.name]: event.target.value});
const{enabled, name, custom1} = input;
const handelSubmit = evt => {
console.log(input);
updateData(input);
};
function handleBoolean(ev) {
const setBool = ev.toString();
console.log(setBool);
setInput({...input, enabled: ev}); //<-- I wasn't updating the enabled prop event here and now I make a copy of all inputs and set the event of enabled... VOILA! is working :)
}
Thanks agan #DrewReese for helping me understand that
Related
FieldSelect component from Sharetribe docs is returning me a string while FieldCheckbox is returning a JSON object.
I want FieldSelect to save a JSON object in a particular case.
How do I do this?
Following is my code for reference,
I am new to REACT and would be really thankful to anyone explaining why this is happening.
Code Calling
<FieldSelect
name={subGroupCategoryKey}
id={subGroupCategoryKey}
label={categoryLabel}
>
{relevantSubGroupCategoryOptions.map(c => (
<option key={c.key} value={c.key}>
{c.label}
</option>
))}
</FieldSelect>
FieldSelect
import React from 'react';
import PropTypes from 'prop-types';
import { Field } from 'react-final-form';
import classNames from 'classnames';
import { ValidationError } from '../../components';
import css from './FieldSelect.module.css';
const FieldSelectComponent = props => {
const { rootClassName, className, id, label, input, meta, children, ...rest } = props;
if (label && !id) {
throw new Error('id required when a label is given');
}
const { valid, invalid, touched, error } = meta;
// Error message and input error styles are only shown if the
// field has been touched and the validation has failed.
const hasError = touched && invalid && error;
const selectClasses = classNames(css.select, {
[css.selectSuccess]: valid,
[css.selectError]: hasError,
});
const selectProps = { className: selectClasses, id, ...input, ...rest };
const classes = classNames(rootClassName || css.root, className);
return (
<div className={classes}>
{label ? <label htmlFor={id}>{label}</label> : null}
<select {...selectProps}>{children}</select>
<ValidationError fieldMeta={meta} />
</div>
);
};
FieldSelectComponent.defaultProps = {
rootClassName: null,
className: null,
id: null,
label: null,
children: null,
};
const { string, object, node } = PropTypes;
FieldSelectComponent.propTypes = {
rootClassName: string,
className: string,
// Label is optional, but if it is given, an id is also required so
// the label can reference the input in the `for` attribute
id: string,
label: string,
// Generated by final-form's Field component
input: object.isRequired,
meta: object.isRequired,
children: node,
};
const FieldSelect = props => {
return <Field component={FieldSelectComponent} {...props} />;
};
export default FieldSelect;
FieldCheckbox
import React from 'react';
import { node, string } from 'prop-types';
import classNames from 'classnames';
import { Field } from 'react-final-form';
import css from './FieldCheckbox.module.css';
const IconCheckbox = props => {
const { className, checkedClassName, boxClassName } = props;
return (
<SVG >
</svg>
);
};
IconCheckbox.defaultProps = { className: null, checkedClassName: null, boxClassName: null };
IconCheckbox.propTypes = { className: string, checkedClassName: string, boxClassName: string };
const FieldCheckboxComponent = props => {
const {
rootClassName,
className,
svgClassName,
textClassName,
id,
label,
useSuccessColor,
onChange: handleChange,
...rest
} = props;
const classes = classNames(rootClassName || css.root, className);
// This is a workaround for a bug in Firefox & React Final Form.
// https://github.com/final-form/react-final-form/issues/134
const handleOnChange = (input, event) => {
const { onBlur, onChange } = input;
onChange(event);
onBlur(event);
handleChange && handleChange(event);
};
const successColorVariantMaybe = useSuccessColor
? {
checkedClassName: css.checkedSuccess,
boxClassName: css.boxSuccess,
}
: {};
return (
<span className={classes}>
<Field type="checkbox" {...rest}>
{props => {
const input = props.input;
return (
<input
id={id}
className={css.input}
{...input}
onChange={event => handleOnChange(input, event)}
/>
);
}}
</Field>
<label htmlFor={id} className={css.label}>
<span className={css.checkboxWrapper}>
<IconCheckbox className={svgClassName} {...successColorVariantMaybe} />
</span>
<span className={classNames(css.text, textClassName || css.textRoot)}>{label}</span>
</label>
</span>
);
};
FieldCheckboxComponent.defaultProps = {
className: null,
rootClassName: null,
svgClassName: null,
textClassName: null,
label: null,
};
FieldCheckboxComponent.propTypes = {
className: string,
rootClassName: string,
svgClassName: string,
textClassName: string,
// Id is needed to connect the label with input.
id: string.isRequired,
label: node,
// Name groups several checkboxes to an array of selected values
name: string.isRequired,
// Checkbox needs a value that is passed forward when user checks the checkbox
value: string.isRequired,
};
export default FieldCheckboxComponent;
When a form is submitted the name of the input is key and the value is the value of user's input.
FieldSelect is a Final Form wrapper for HTML <select name="someName"> element. That element can only have a single value selected at the time, so the submit will contain something like someName: 'valueOfSelectedOption'.
FieldCheckbox is a wrapper for HTML <checkbox> element. Final Form library uses pretty widely used "array" setup for checkboxes that have the same name.
I mean, if your form has something like <checkbox name="asdf" value="a"/><checkbox name="asdf" value="b"/>, and user checks both of those checkboxes, the submitted value would look like this: asdf: ["a", "b"].
Note: Without Final Form library, the default HTML submit output would have been asdf=a&asdf=b.
So, FieldCheckbox is actually using array as an output format instead of JSON (although, they look the same in this case).
Here are a couple of links to React Final Form that you might want to check:
https://final-form.org/docs/react-final-form/
http://jinno.io/app/12/?source=react-final-form
If you want to change the field's value to something else than what it becomes by default, you should check parse (and format) field props:
https://final-form.org/docs/react-final-form/types/FieldProps#parse
https://final-form.org/docs/react-final-form/types/FieldProps#format
I have a form with 3 input filed FIRST NAME, LAST NAME, NUMBER. So once forms open there is a particular set of pre filled values coming from backend. So, if i try to add validation check for my firstname and if user makes the filed blank and click submit it shows me undefined in console for the respective firstname value. So, could someone help to add validation check for firstname, last name and number ??
const EditContact = inject('Contacts')(observer((props) => {
const { text, Contactfirst, apptContacts } = props;
const { updateContact } = apptContacts;
const CMS = text.appointmentManager.editAppointmentContact;
const [state, setState] = React.useState({
data: {
firstName: Contactfirst[0],
lastName: Contactfirst[1],
number: Contactfirst[2],
},
firstNameValid: '',
lastNameValid: '',
numberValid: '',
});
const handleTextareaChange = (event) => {
const { data } = state;
data[event.target.name] = event.target.value;
setState({
data,
});
};
const valid = () => {
if (state.data.firstName !== 'undefined') {
setState({ firstNameValid: 'Required.Please enter your given name.' });
}
};
const saveButton = () => {
if (valid()) {
const personName = `${state.data.firstName} ${state.data.lastName}`;
const primaryContact = {
name: personName,
phoneNumber: state.data.number,
};
}
};
return (
<div>
<VerticalSpacing size={ABLE_SPACING_SIZE.spacing4x} />
<h1 tabIndex="-1" className="HeadingB mt-sheet-heading">
{CMS.heading1}
</h1>
<VerticalSpacing size={ABLE_SPACING_SIZE.spacing3x} />
<TextField id="givenName" name="firstName" label={CMS.name} onChange={handleTextareaChange} value={(state.data.firstName !== 'undefined') ? state.data.firstName : ''} />
<p>{state.firstNameValid}</p>
<VerticalSpacing size={ABLE_SPACING_SIZE.spacing3x} />
<TextField id="familyName" name="lastName" label={CMS.familyName} onChange={handleTextareaChange} value={(state.data.lastName !== 'undefined') ? state.data.lastName : ''} />
<p>{state.lastNameValid}</p>
<VerticalSpacing size={ABLE_SPACING_SIZE.spacing3x} />
<TextField id="mobileNumber" name="number" label={CMS.mobile} onChange={handleTextareaChange} value={(state.data.number !== 'undefined') ? state.data.number : ''} />
<p>{state.numberValid}</p>
<VerticalSpacing size={ABLE_SPACING_SIZE.spacing4x} />
<ActionButton className={styles.saveCta} variant="HighEmphasis" label={CMS.saveCTA} onClick={() => saveButton()} />
</div>
);
}));
export default EditContact ;
Handling form state and validation is a problem many of us have faced. If you are having trouble solving the issue on your own I would suggest taking look at a library that handles the heavy lifting for you. One such library is Formik https://formik.org/docs/overview
Here is a minimal example that illustrates it in action:
https://codesandbox.io/s/zkrk5yldz
If you want to use material ui for your components that is easy to apply validation to your text fields.
Just follow this link
https://mui.com/components/text-fields/#validation
I am trying to show the error message onClick event of DatePicker. But it is working when we click on the datepicker input box. But when we are clicking on datepicker icon, onClick event not working.
Can anyone please suggest any event to show the warning message on click of icon as well.
I am using below component:
import { DatePicker } from 'office-ui-fabric-react';
<div className="date-range">
<DatePicker
label={text.label}
placeholder={text.placeholder}
id={text.id}
showMonthPickerAsOverlay
strings={DayPickerStrings}
onSelectDate={date => onDateRangeChange(date, text.id)}
onClick={event => onDateChangeClick(event)}
isMonthPickerVisible={false}
formatDate={date => onFormatDate(date, pattern)}
minDate={minDate}
maxDate={maxDate}
value={value[text.id]}
/>
{showEndDateWarning ? (
<p className="warningClass">{text.warningMsg}</p>
) : null}
</div>
you can change the state value of "showEndDateWarning" to true on "onSelectDate" event.
I've just had the same issue and resolved it by using onClickCapture=
import * as React from 'react';
import {
DatePicker,
IDatePickerStrings,
defaultDatePickerStrings,
IDatePickerStyles
} from '#fluentui/react';
import { useConst } from '#fluentui/react-hooks';
import { AcknowledgementReIssueResponse } from '../models/dtos';
// START COMPONENT
const datePickerStyles: Partial<IDatePickerStyles> = { root: { maxWidth: 200, marginTop: -10 } };
export const ReIssueOnPicker = (props: { acknowledgementReIssueResponse: AcknowledgementReIssueResponse, disabled: boolean, onPress: (controlName : string ) => void }) => {
console.log('ReIssueOnPicker');
console.log(props);
const minDate = new Date(props.acknowledgementReIssueResponse.dueFrom);
const zeroPad = (num, places) => String(num).padStart(places, '0')
const onSelectDate = React.useCallback(
(date: Date): void => {
// toISODate applied browser local time offset ?
props.acknowledgementReIssueResponse.reIssueOn = `${date.getFullYear()}-${zeroPad(date.getMonth() + 1, 2)}-${zeroPad(date.getDate(), 2)}T00:00:00.0000000`;
}, null
);
const strings: IDatePickerStrings = useConst(() => ({
...defaultDatePickerStrings,
}));
return (
<div>
<DatePicker
styles={datePickerStyles}
// DatePicker uses English strings by default. For localized apps, you must override this prop.
onSelectDate={onSelectDate}
onAfterMenuDismiss={() => props.onPress('')}
**onClickCapture**={event => props.onPress('ReIssueOnPicker')}
disabled={props.disabled}
strings={strings}
placeholder="Select a date..."
ariaLabel="Select the date to re-issue on"
minDate={minDate}
allowTextInput
value={minDate}
/>
</div>
);
};
In react-tag-autocomplete lib, need to add manual suggestions.
So, I created new component called ReactTagsSuggestions for suggestions list.
How can I clear the input field of ReactTags
<ReactTags
id="form-share"
tags={selectedTags}
// suggestions={tagSuggestions}
handleAddition={this.props.handleAddition}
handleDelete={this.props.handleDelete}
placeholder={this.props.tags.length ? '' : tr('Share with users, groups, and channels')}
tagComponent={this.selectedTags}
handleInputChange={this.handleInputChange}
autofocus={false}
/>
<ReactTagsSuggestions
suggestionList={tagSuggestions}
showSuggestions={tagSuggestions.length > 0}
checkToShare={this.props.checkToShare}
/>
How about if you create a function like this
handleDelete = () => {
this.setState({ tags: [] })
}
and pass this to the ReactTags component
<ReactTags
.
.
handleDelete={ this.handleDelete }
/>
?
To clean the input field of ReactTags you can reset the selectedTags to an empty array:
clearTag() {
this.setState({ selectedTags: [] });
}
// Call the clearTag somwhere in your app
<button type="button" onClick={this.clearTag}>Clear tags</button>
Example: https://codesandbox.io/s/react-tags-v9mft
It is too late, but may be it will be useful to others. I think you mean clearing input value instead of tags. Here is solution with useState and inputValue prop of ReactTags
const [inputValue, setInputValue] = useState('');
<ReactTags
inputValue={inputValue}
handleAddition={(...args) => {
myHandleAddition(args);
setInputValue('');
}}
handleInputChange={(tag) => {
if (tag.slice(-1) === ' ') {
myHandleAddition([{ id: tag, text: tag }]);
setInputValue('');
} else {
setInputValue(tag);
}
}}
/>
On an <EditView /> I am using <ReferenceArrayField /> to display an edit form of checkbox.
I need to fill the form with the data, but the checkbox list is so far implemented using the response entities from the ReferenceArrayField.
I have an the user entity fetched by the <EditView/>
In <EditView/> I am fetching related data using `
I write a custom grid using the <ReferenceArrayField /> data
I want to fill the checked props of my checkbox depending on values present in user.
This would be some trivial controller logic for the UI but I can't find how to do it.
So far I am only able to have one record by scope which means that every data displayed on the UI would be from a single entity and no controller logic can be written in between.
Use case
const CheckboxGrid = ({ ids, data, basePath }) => (
<div style={{ margin: '1em' }}>
{ids.map(id =>
<div key={id}>
<h2><TextField record={data[id]} source="description" /></h2>
<label>
<input type="checkbox" name="authority[]" disabled checked={true} />{' '}
<TextField record={data[id]} source="role.description" />
</label>
{data[id].siteFunctionList.map((siteFunction) => (
<div key={siteFunction.description} className="pl-3">
<h3><TextField record={siteFunction} source="description" disabled /></h3>
<label>
<input type="checkbox" name="authority[]" disabled />{' '}
<TextField record={siteFunction} source="role.description" />
</label>
</div>
))}
</div>
)}
</div>
);
CheckboxGrid.defaultProps = {
data: {},
ids: [],
};
export class AuthorityList extends React.Component {
render() {
const resourceName = 'users';
const resourceId = this.props.match.params.id;
return (
<Edit {...this.props} resource={resourceName} id={resourceId}>
<SimpleForm>
<ReferenceArrayField reference="siteServices" source="siteServiceIdList">
<CheckboxGrid />
</ReferenceArrayField>
</SimpleForm>
</Edit>
);
}
}
The user have a list of values that would match some of the values present in data.
Fact
I do not have access to the <Edit .../> record in the scope of checkbox grid.
I have seen that there is in redux a form['record-form'].values where the entity is store so I could access it with custom selectors:
EX:
const mapStateToProps = createStructuredSelector({
record: makeSelectFormRecordFormValues(),
});
const withConnect = connect(mapStateToProps);
const CheckboxGridConnect = compose(
withConnect,
)(CheckboxGrid);
There is also a store for each resources declared in admin on rest. It store the entities fetched with <ReferenceArrayField /> but I don't see these stores scalable in case we use more than one <ReferenceArrayField /> for the same resource.
Should I directly pick in the store and inject my self the entities ?
Please find below my hacky code for SelectInput. I have made this to basically edit an array of values. The API detects any changes in the array of values and updates the references accordingly. Below is a copy paste job from the AOR core and then some modifications for my use case.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import get from 'lodash.get';
import SelectField from 'material-ui/SelectField';
import MenuItem from 'material-ui/MenuItem';
export class CustomSelectInput extends Component {
/*
* Using state to bypass a redux-form comparison but which prevents re-rendering
* #see https://github.com/erikras/redux-form/issues/2456
*/
state = {
value: this.props.fields.get(this.props.selectionOrder) ? this.props.fields.get(this.props.selectionOrder) : null //selectionOrder is a prop I am passing to the component manually. It is very specific to my use case
}
handleChange = (event, index, value) => {
this.props.fields.remove(this.props.selectionOrder) //using redux-form fields methods to remove items and adding new ones. SelectionOrder now becomes an unfortunate hard coding of the particular value this selectInput is supposed to edit in the array of values
this.props.fields.push(value);
this.setState({ value });
}
renderMenuItem = (choice) => {
const {
optionText,
optionValue,
} = this.props;
const choiceName = React.isValidElement(optionText) ? // eslint-disable-line no-nested-ternary
React.cloneElement(optionText, { record: choice }) :
(typeof optionText === 'function' ?
optionText(choice) :
get(choice, optionText)
);
return (
<MenuItem
key={get(choice, optionValue)}
primaryText={choiceName}
value={choice.name}
/>
);
}
render() {
const {
allowEmpty,
choices,
elStyle,
input,
isRequired,
label,
meta: { touched, error },
options,
resource,
source,
} = this.props;
return (
<SelectField
value={this.state.value}
onChange={this.handleChange}
autoWidth
style={elStyle}
errorText={touched && error}
{...options}
>
{allowEmpty &&
<MenuItem value={null} primaryText="" />
}
{choices.map(this.renderMenuItem)}
</SelectField>
);
}
}
CustomSelectInput.propTypes = {
addField: PropTypes.bool.isRequired,
allowEmpty: PropTypes.bool.isRequired,
choices: PropTypes.arrayOf(PropTypes.object),
elStyle: PropTypes.object,
input: PropTypes.object,
isRequired: PropTypes.bool,
label: PropTypes.string,
meta: PropTypes.object,
options: PropTypes.object,
optionText: PropTypes.oneOfType([
PropTypes.string,
PropTypes.func,
PropTypes.element,
]).isRequired,
optionValue: PropTypes.string.isRequired,
resource: PropTypes.string,
source: PropTypes.string
};
CustomSelectInput.defaultProps = {
addField: true,
allowEmpty: false,
choices: [],
options: {},
optionText: 'name',
optionValue: 'id'
};
export default CustomSelectInput