react-bootstrap how to use custom form control component - reactjs

I need to add select input in my form, but there are too many options, so the default way to do it looks really ugly(
<Form.Control as="select">
{props.options.map(
(o) => <option>{o}</option>
)}
</Form.Control>
So I've decided to use react-bootstrap-typeahead because it's already being used in my project and it supports search. However, it didn't work properly in that case.
<Form>
<Form.Group>
<Form.Label>Provider</Form.Label>
<Form.Control
as={Typeahead}
id="id"
options={props.options}
/>
</Form.Group>
</Form>
It looks like this:
Might I've done it not properly, but I can't figure out how to do it in another way(

https://react-bootstrap.github.io/components/forms/#form-control-props
Ther has a props "as". See exemle bellow.
export default function Input(props) {
const CustomFormControl = React.forwardRef(({ children, onChange}, ref) => (
<>
<p>Insert your elements for yout form</p>
<p>{props.test}</p>
</>
));
return (
<>
<Form.Control
as = {CustomFormControl}
test = {"hellow world"}
/** and here u can inser the props you need **/
/>
</>
}

Related

React-Hook-Form conditional fields in a map function

I'm curious if anyone has used conditional fields in react-hook-from but within a map function. I've got the basic rendering happening, but since it is in a list and they all refer to the same .map() criteria, it is populating the conditional input for all fields, regardless if they are checked. Heres the basic idea for using conditional fields in RHF: https://codesandbox.io/s/react-hook-form-conditional-fields-qgr41
Heres what I've got so far:
{goals && goals.map((goal) => (
<>
<FormControlLabel
control={
<Switch
key={goal.title}
//id..? marked? title?
{...register('marked')}
name='marked'
value={goal.marked}
/>}
label={goal.title}
/>
<br />
{marked && (
<>
<Input
key={goal.title}
{...register('goal.note')}
id="note"
type='text'
label="Progress Note"
name="note"
onChange={(e) => {
e.target.value = e.target.value
}}
/>
<br />
</>
) }
The problem is that from the UI side, when I toggle the switch (or mark a checkbox), that all the fields populated in the map are referring to the same prop in the switch, 'marked'. So when toggling for 1, it populates the additional input field for all elements in the map function.
An additional issue I have yet to look into is passing the data for the individual fields to the data collected by the form for the submit. Right now, my assumption is that all fields would be treated as one value, since the form is recognizing them all together. So ideally if one item in the map is toggled and additional info is provided, then only those values are passed to the form for the submit, rather than all fields (toggled or not) being passed.
UPDATE
So I got the toggle functioning by creating a child component in order to more easily manage the state of the toggle, and leveraging the index within the props so the browser would not treat each conditional field as the same:
parent component:
{goals && goals.map((goal, index) => (
<GoalInput
goal={goal}
index={index}
register={register}
control={control}
errors={errors}
/>
new child component:
function GoalInput({ goal, index, register, control, errors }) {
const [toggle, setToggle] = useState(false)
return (
<>
<FormControlLabel
control={
<Switch
key={index}
{...register(`goals.${index}.marked`)}
checked={toggle}
name={`goals.${index}.marked`}
value={toggle}
onChange={() => setToggle(!toggle)}
/>
}
label={goal.title}
/>
<br />
{toggle ? (
<>
<Controller
control={control}
name={`goals.${index}.note`}
id={`goals.${index}.note`}
render={({field}) => (
<Input
type='text'
index={index}
error={!!errors.note}
value={field.value}
onChange={(e)=>field.onChange(e)}
label="Progress Note"
/>
)}
/>
<br />
</>
) : <></>}
</>
)
}
So the toggles work independently, appropriately record whether the value of the toggle is true/false, and the subsquent conditional input in being tracked as well. Still have some struggles with the data being passed correctly through to the backend, but that is another issue. Hope this helps anyone coming along.

Updating multi-select options from state doesn't trigger material_select so options don't appear

I am updating the options of the select based on the state of my component.
The state is initially set to an empty array but once loaded from an API, the state is updated so the options should update.
While this does happen (and can be seen in the dev tools), it looks like material_select needs to be called to update how it looks.
I can get this to appear correctly by calling $("select").material_select() from the console.
I think that the line causing the issue is here: https://github.com/react-materialize/react-materialize/blob/master/src/Input.js#L38
Is there a way that I can manually call this while inside my component?
Please note that I'm using create-react-app.
<Row>
<Input
id="categories"
type="select"
label="Categories"
multiple={true}
onChange={this.handleChange}
s={12}
>
{this.state.categories.map(category => {
return (
<option key={category._id} value={category._id}>
{category.name}
</option>
);
})}
</Input>
</Row>
You can fix the problem by rendering the Input component once your options arrive from API.
You can initailize your state as null as opposed to empty array:
state = {
categories: null;
}
So in your render method, render the component conditionally (only if options are available):
render() {
return (
<Row>
{this.state.categories ? (
<Input
id="categories"
type="select"
label="Categories"
multiple={true}
onChange={this.handleChange}
s={12}
>
{this.state.categories.map(category => {
return (
<option key={category._id} value={category._id}>
{category.name}
</option>
)
})}
</Input>
) : null}
</Row>
)
}

Access current record in admin on rest edit

I need to access the current record in admin on rest Edit. I have an entity and need one of it's properties to do a conditional task. I searched all over the internet and found nothing.
export const UserEdit = (props) => (
<Edit {...props}>
<SimpleForm>
<TextField source="Username" label="Username" />
</SimpleForm>
</Edit>
);
This is not possible with admin-on-rest but this is where React comes to the rescue, you can create a component to add this behaviour.
Something like:
const WithProps = ({children,...props}) => children(props);
And use it like this:
export const UserEdit = (props) => (
<Edit {...props}>
<WithProps>{({record,...props})=>
<SimpleForm record={record} {...props}>
<TextField source="Username" label="Username" />
</SimpleForm>}
</WithProps>
</Edit>
);
Are you looking for aor-dependent-input?
https://github.com/marmelab/aor-dependent-input
It allows to display an input depending on other inputs values. It's authored and supported by the admin-on-rest core team.
Normally you don't have an access to a record inside a SimpleForm.
Perhaps, you can define a custom field and wrap a TextField inside it with access to the record so as to perform a conditional check as desired.
ex:
const CustomTextField = ({record = {}, source}) => (
{ record.foo ? <TextField source="username" label="username" /> : <WhateverField /> }
);
and then, use the CustomTextField in your UserEdit instead.

React-bootstrap FormControl Select placeholder

Trying to set up a control for a select field but even with placeholder set it still always auto selects the first item in the list.
It looks like this...
export function renderSelector({input, label, placeholder, meta:{touched, warning, error}, title, mandatory, fieldValues, fieldItemKeyFunc, fieldItemLabelFunc, props}) {
const mappedData = fieldValues.map(v => ({Id: fieldItemKeyFunc(v), Name: fieldItemLabelFunc(v)}));
let custom = props || {};
return (
<FormGroup controlId={input.name} validationState={touched && error ? 'error' : null}>
<Col xs={12}>
{renderLabel(title, label, mandatory)}
</Col>
<Col xs={12}>
<FormControl componentClass="select" placeholder={placeholder} name={input.name} {...input} {...custom}>
{
mappedData.map((item, index) => {
return (
<option key={index} value={item.Id}>{item.Name}</option>
)
})
}
</FormControl>
<FormControl.Feedback/>
{renderErrBlock(touched, warning, error)}
</Col>
</FormGroup>
);
}
When this renders the selector always starts with the first item selected, instead of the placeholder. How can I fix this?? I tried just adding a default first option and adjusting the css, but for Accessibility that doesn't work that well because the contrast levels
A bit late, but check if adding this option helps you:
<option key='blankChoice' hidden value />
Make sure to check the form value to a blank choice as well:
<Form.Control
value=''
>
You can do something like this:
Add a
<option disabled value={-1} key={-1}>
, and set your
<FormControl value={-1} ...
for a similar effect.
Just dropping a late answer, in case someone finds this useful.
React Bootstrap's Form as='select' doesn't seem to allow placeholder out of the box. Adding a blank value to Form.Control would render the input useless. Adding an extra option above mappedData.map(), as follows would work:
<option key = 'blankChoice' hidden value> --Your placeholder text-- </option>

Redux-Form—Create Password Field with »retype«

I am building a redux form and I would like to create a »Retype Password« field which is rendered next to the password field. I have this code:
const renderRow = (field) => {
return (
<div className={`form-row ${field.type}`}>
<label>{field.label}</label>
<Field field={field} component={renderPassword} name={field.name} />
</div>
);
}
const renderPassword = ({field, input, meta: { error, touched }}) => {
return (
<div className="password-inputs-wrap">
<input type="password" {...input} />
<input type="password" {...input} name="password-retype" />
{touched && error && <RenderError error={error} />}
</div>
);
}
Right now this renders two password fields, but changing one, changes the other at the same time.
The renderRow is a bit simplified here, since the component for that field is not hard coded as show here. So this password field is an exception, since it consists of two <input>s. For all other fields, which consist of a single <input>/<select />, etc, it works fine.
How can I »decouple« this retype <input> from the first one?
You need two fields (two Fields).

Resources