Hi there, I’m wondering what is the correct way to get a controller component working with redwood forms.
I feel like I have everything setup right but I can not get the controller to register and I’m not seeing any data.
Here is my code
import { useForm, Controller } from 'react-hook-form'
import Select from 'react-select/creatable'
export const QUERY = gql`
query FindPlants {
plants {
id
title
}
}
`
export const Loading = () => <div>Loading...</div>
export const Empty = () => <div>Empty</div>
export const Failure = ({ error }) => (
<div style={{ color: 'red' }}>Error: {error.message}</div>
)
export const Success = ({ plants }) => {
const methods = useForm()
return (
<Controller
control={methods.control}
defaultValue={[]}
name="field_name_product"
render={({ field }) => {
console.log(field)
const { onChange, value, name, ref } = field
return (
<Select
name={name}
inputRef={ref}
options={plants}
value={plants.find((c) => c.id === value)}
onChange={(val) => {
onChange(val)
}}
getOptionValue={(option) => option.id}
getOptionLabel={(option) => option.title}
className="basic-multi-select"
classNamePrefix="select"
isMulti
/>
)
}}
/>
)
}
I’m importing this into a standard redwood form, but the data is never submitted.
I have also tried putting the controller component directly in the form and that did not work either
Do i need to register or something?
Thanks
Shannon
Hi Shannon! Welcome to the Redwood community!
If I understand your problem correctly, when you submit with form the Controller there’s no data. Is that right?
Only thing I’d change is to wrap with <form>
. Something like this:
<form onSubmit={methods.handleSubmit((data) => console.log(data))}>
// <Your Controller magic here>
<button type="submit">Submit</button>
</form>
Here’s a short tape to show you what I’m seeing: https://s.tape.sh/R7uB0cmD
Here’s the full code of the cell in this tape: https://gist.github.com/callingmedic911/07ffe53217a09339c842f631c9e084ac
Here’s an example from react-hook-forms using react-select: https://react-hook-form.com/get-started/#integratingwithuilibraries
Let me know if missed anything. I’d be glad to help.
Thanks for the quick reply. The issue I am having is more around using the controller in an existing redwood form.
Here is a quick loom video to show the problem Loom | Free Screen & Video Recording Software
And some more complete code here:
import {
Form,
FormError,
FieldError,
Label,
TextField,
Submit,
} from '@redwoodjs/forms'
import { useForm, Controller } from 'react-hook-form'
import Select from 'react-select/creatable'
const NoteForm = (props) => {
const options = [
{ id: 1, title: 'plant 1' },
{ id: 2, title: 'plant 2' },
]
const methods = useForm()
const onSubmit = (data) => {
console.log(data)
props.onSave(data, props?.note?.id)
}
return (
<div className="rw-form-wrapper">
<Form
onSubmit={onSubmit}
// onSubmit={methods.handleSubmit((data) =>
// console.log('data-----', data)
// )}
error={props.error}
>
<FormError
error={props.error}
wrapperClassName="rw-form-error-wrapper"
titleClassName="rw-form-error-title"
listClassName="rw-form-error-list"
/>
<Label
name="description"
className="rw-label"
errorClassName="rw-label rw-label-error"
>
Description
</Label>
<TextField
name="description"
defaultValue={props.note?.description}
className="rw-input"
errorClassName="rw-input rw-input-error"
validation={{ required: true }}
/>
<FieldError name="description" className="rw-field-error" />
{/* <PlantSelectCell /> */}
<Controller
control={methods.control}
defaultValue={[]}
name="field_name_product"
render={({ field }) => {
console.log(field)
const { onChange, value, name, ref } = field
return (
<Select
name={name}
inputRef={ref}
options={options}
value={options.find((c) => c.id === value)}
onChange={(val) => {
onChange(val)
}}
getOptionValue={(option) => option.id}
getOptionLabel={(option) => option.title}
className="basic-multi-select"
classNamePrefix="select"
isMulti
/>
)
}}
/>
<div className="rw-button-group">
<Submit disabled={props.loading} className="rw-button rw-button-blue">
Save
</Submit>
</div>
</Form>
</div>
)
}
export default NoteForm
Got it! Thanks for the detailed explanation.
I think the Form
and the Controller
within are referring to the different useForm()
instance. Since Redwood form uses FormProvider
, you can get rid of useForm
in the cell and remove the prop control
from the Controller
.
Let me know if this resolves your issue.
Perfect Thanks so much @callingmedic911
1 Like
@shansmith01 @callingmedic911
Hi,
I have a very similar question, I was looking for exactly this and solution with NOT using separate controller helped.
I have slightly different problem:
- When I try to submit data the ‘plants’ from here are sent as array of objects instead of array of ID’s (as I think it should be sent)
- If I change the
onChange
method to return ID’s the react-select
breaks with undefined values (as title
and id
fields do not exist
- In either case the GraphQL schema for my update mutation is not expecting that type to be present on the update input and throws an error.
So here’s a question:
A. what can I do to make GraphQL aware that when I update main object (say Garden) I want to assign multiple Plants to it? Since the schema is generated I should pick up changes I made to EditGardenCell
or am I missing something?
B. Assuming I will need to send only IDs to the GraphQL endpoint (like with connect) how can I do this without breaking react-select
?
input UpdateGardenInput {
description: String
title: String
}
import Select from 'react-select';
import { Controller } from '@redwoodjs/forms';
// use this as data for the Select field
// const options = [
// { id: 'chocolate', title: 'Chocolate' },
// { id: 'strawberry', title: 'Strawberry' },
// { id: 'vanilla', title: 'Vanilla' },
// ];
const SelectMultipleChoice = ({
name = 'plants',
options = [],
defaultValues,
control,
}) => {
return (
<Controller
name={name}
render={({ field }) => {
return (
<Select
{...field}
options={options}
defaultValue={defaultValues}
getOptionValue={(option) => option?.id}
getOptionLabel={(option) => option?.title}
isMulti
onChange={(val) => {
field.onChange(val);
}}
// this will return the IDs to GraphQL (but it will break react-select) with name of form input - here `plants`
// onChange={(val) => {
// return field.onChange(
// val.map((element) => {
// return element.id;
// })
// );
// }}
/>
);
}}
/>
);
};
export default SelectMultipleChoice;