It's a basic understanding required working with (CRUD) data is to be able to populate a SELECT drop down menu with data from the database. I've seen many tutorials and articles online but their examples in many cases are contrived.
When it comes to CRUD (Create, Read, Update and Delete) you need to account for:
And thirdly, for a drop-down menu with multiple choice options. Which is why I say many examples are contrived because the articles never explore beyond the simplest examples.
Let's take a look at an easy example first (Create) and following on the Update.
In this example there are two props. The data comes from a Laravel controller on the backend.
const { teams } = usePage().props;
const { clients } = usePage().props;
There is one client
to a Project but many teams
to a Project. So, there is a need for a simple SELECT and a more complex SELECT menu. Each SELECT requires an event handler, as seen below.
function handleTeamsSelect(e) {
let values = Array.from(e.target.selectedOptions, option => option.value);
setData("teams", values);
}
function handleClientsSelect(e) {
setData("client_id", e.target.selectedOptions[0].value);
}
In my experience it's better (more enjoyable) to work with controlled inputs (backed up by state) than it is with uncontrolled inputs. Your opinion may differ of course.
The form in question is below, showing only the SELECTs we're concerned with, for brevity.
<div className="mt-4">
<InputLabel htmlFor="teams" value="Team Selection" />
<Select
className="mt-1 block w-full"
size={ 10 }
multiple={ true }
name="teams"
handleSelect={ handleTeamsSelect }
rows={ teams }>Team Selection</Select>
<InputError message={ errors.teams } className="mt-2" />
</div>
<div className="mt-4">
<InputLabel htmlFor="client_id" value="Client Selection" />
<Select
className="mt-1 block w-full"
size={ 10 }
multiple={ false }
name="client_id"
handleSelect={ handleClientsSelect }
rows={ clients }>Client Selection</Select>
<InputError message={ errors.client_id } className="mt-2" />
</div>
The <Select /> component comes next.
export default function Select({ className = '', size, multiple, name, handleSelect, value, rows, children, isFocused = false }) {
useEffect(() => {
if(isFocused) {
input.current.focus();
}
}, []);
return (
<select
className={
'border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm ' + className}
size={ size }
multiple={ multiple }
name={ name }
onChange={ handleSelect }
value={ value }>
<option value="0">{ children }</option>
{
rows.map((row) => (
<option key={ row.id } value={ row.id }>{ row.name }</option>
))
}
</select>
);
}
I use the same <Select /> component for both single and multiple-choice option menus. That pretty much covers most general SELECTs with React and Inertia. Recalling at the start there are two use cases. We've covered the first and now we cover the second (Update) use case.
The event handlers clearly remain the same albeit with additional props passed on from the backend.
const { project } = usePage().props;
const { clients } = usePage().props;
const { teams } = usePage().props;
const { defaults } = usePage().props;
const { data, setData, post, processing, errors } = useForm({
name: project.name,
description: project.description,
teams: defaults,
client_id: project.client_id,
id: project.id,
_method: "put",
});
I've included Inertia's useForm
hook usage as well. Those props should be obvious what they are? Except may the defaults
prop which happens to be the rows of data previously SELECTed.
There are slight differences in the form for an Update compared to a Create and they're shown below.
<div className="mt-4">
<InputLabel htmlFor="teams" value="Team Selection" />
<Select
className="mt-1 block w-full"
size={ 10 }
multiple={ true }
name="teams"
handleSelect={ handleTeamsSelect }
value={ data.teams }
rows={ teams }>Team Selection</Select>
<InputError message={ errors.teams } className="mt-2" />
</div>
<div className="mt-4">
<InputLabel htmlFor="client_id" value="Client Selection" />
<Select
className="mt-1 block w-full"
size={ 10 }
multiple={ false }
name="client_id"
handleSelect={ handleClientsSelect }
value={ data.client_id }
rows={ clients }>Client Selection</Select>
<InputError message={ errors.client_id } className="mt-2" />
</div>
With an Update you must set the values. The defaults
are given over to the form data by (pun intended) default. That ensures when the SELECT in question is rendered the options are SELECTed.
Looking at the backend we can see what's happening a little more clearly.
$project = Project::where([
'id' => $project->id,
'company_id' => auth()->guard('web')->user()->company_id,
])->firstOrFail();
$teams = Team::where([
'company_id' => auth()->guard('web')->user()->company_id,
])
->get();
$clients = Client::where([
'company_id' => auth()->guard('web')->user()->company_id,
])
->get();
$project->load(['teams']);
$defaults = $project->load(['teams'])->teams->pluck('id');
return inertia('User/Project/Edit', [
'project' => $project,
'clients' => $clients,
'teams' => $teams,
'defaults' => $defaults,
'breadcrumbs' => Breadcrumbs::generate('projects.edit', $project),
]);
For the default options selected we are only interested in the primary keys. That more or less concludes this post about using React with Inertia and dynamic SELECTs.
I've enjoyed working with Inertia thus far and intend to continue using it and can't wait until my next project I can consider Inertia 2 for it.
Content on this site is licensed under a Creative Commons Attribution 4.0 International License. You are encouraged to link to, and share but with attribution.
Copyright ©2024 Leslie Quinn.