vincent15000's avatar

InertiaJS and dynamic select / options fields

Hello,

I want the user be able to type a zipcode and then the select / options list fills dynamically according to the zipcode.

    watchEffect(async () => {
        if (zipcode.value.length >= 3) {
            console.log(zipcode.value.length);
            await router.get(route('trainings.create'), { zipcode: zipcode.value }, { only: ['cities'], preserveState: true });

            filteredCities.value = props.cities;
        } else {
            filteredCities.value = [];
        }
    });

Here is my controller.

return Inertia::render('Trainings/Create', [
    'themes' => Theme::orderBy('name')->get()->map(function ($theme) {
        return [
            'id' => $theme->id,
            'label' => $theme->name,
        ];
    }),
    // 'cities' => Inertia::optional(function () use ($zipcode) {
    //     return City::when($zipcode, function ($query) use ($zipcode) {
    //         $query->where('zipcode', 'like', '%'.$zipcode.'%');
    //     })->orderBy('name')->get()->map(function ($city) {
    //         return [
    //             'id' => $city->id,
    //             'label' => $city->zipcode.' '.$city->name,
    //         ];
    //     });
    // }),
    'cities' => City::
        when($zipcode, function ($query) use ($zipcode) {
            $query->where('zipcode', 'like', $zipcode.'%');
        })
        ->orderBy('name')->get()->map(function ($city) {
            return [
                'id' => $city->id,
                'label' => $city->zipcode.' '.$city->name,
            ];
        }),
]);

With Inertia::optional, it doesn't work, so I have chosen another way.

When I type the zipcode : 1 5 0 0 0, it works fine.

When I delete the zipcode :

  • 1 5 0 0 0
  • 1 5 0 0
  • 1 5 0
  • 1 5
  • 1 5 1 // I get the cities for 150 zipcode and not for 151

Do you have any idea why ?

Thanks for your help.

V

0 likes
1 reply
LaryAI's avatar
LaryAI
AI
Best Answer
Level 58

Certainly! The issue you're experiencing is due to how Inertia handles partial reloads and how props are updated after an Inertia visit. Specifically, when you use router.get with { only: ['cities'] }, Inertia will only update the cities prop, but your component is still using the old props.cities value immediately after the request, before the new data arrives.

The main problem:
You're setting filteredCities.value = props.cities; immediately after the Inertia request, but props.cities hasn't updated yet. This causes a mismatch, especially when deleting or changing the zipcode quickly.

Solution:
You should watch for changes to props.cities and update filteredCities accordingly. This way, whenever Inertia updates the cities prop, your select options will update as well.

Here’s how you can fix it:

1. Watch for changes to props.cities

import { ref, watch, watchEffect } from 'vue';
import { router } from '@inertiajs/vue3';

const zipcode = ref('');
const filteredCities = ref([]);
const props = defineProps(['cities']);

watchEffect(() => {
    if (zipcode.value.length >= 3) {
        router.get(
            route('trainings.create'),
            { zipcode: zipcode.value },
            { only: ['cities'], preserveState: true, replace: true }
        );
    } else {
        filteredCities.value = [];
    }
});

// Watch for changes to props.cities and update filteredCities
watch(
    () => props.cities,
    (newCities) => {
        filteredCities.value = newCities || [];
    },
    { immediate: true }
);

2. Controller: No changes needed

Your controller logic is fine as long as you’re passing the correct filtered cities based on the zipcode.

3. Why this works

  • The router.get triggers a partial reload of the cities prop.
  • The watch on props.cities ensures that filteredCities is always in sync with the latest data from the server, regardless of how fast the user types or deletes.
  • No need to set filteredCities immediately after the request; let the prop update drive your UI.

Summary:
Always update your local state (filteredCities) in response to changes in the Inertia props (props.cities), not immediately after making the request.

Let me know if you need a more complete example or further clarification!

1 like

Please or to participate in this conversation.