inertia icon indicating copy to clipboard operation
inertia copied to clipboard

Component not updating after post request to Laravel from Vue

Open narkan opened this issue 3 years ago • 1 comments

Versions:

Laravel 9 "@inertiajs/inertia": "^0.11.0", "@inertiajs/inertia-vue3": "^0.6.0"

Describe the problem:

  • the Vue component makes a post request to the Laravel backend.
  • the Laravel router calls the controller correctly
  • the controller gets the collection correctly and calls Inertia::render
  • the component doesn't update - no evidence of any activity or errors seen

Steps to reproduce:

// Search.vue
Inertia.post(
    `/centres/search/${props.type}`,
    { search: value },
    {  preserveScroll: true }
);
// web.php
    Route::post('/centres/search/{type}', [CentresController::class, 'search'])->name('centres.search');
// CentresController.php

// THE METHOD CALLED BY THE ROUTER:
public function search(Request $request, string $type)
{
    $search = $request->input('search');

    $filters = new \stdClass();
    $filters->search = $search;

    $this->search_text( $filters );
}

// THIS METHOD DOES NOT SEEM TO BE ABLE TO CALL inertia::render()
public function search_text($filters)
{
    $centres = Centre::query()
        ->when($filters->search, function ($query, $search) {
            $query->where('name', 'like', '%' . $search . '%')
            ->OrWhere('address', 'like', '%' . $search . '%')
            ->OrWhere('email', 'like', '%' . $search . '%')
            ->OrWhere('notes', 'like', '%' . $search . '%');
        })
        ->with('resources')
        ->paginate($this->pagination);


        // dd($centres);   >>>   outputs the correct collection as expected


    // THIS SEEMS TO FAIL, BUT NO ERROR MESSAGES OR EVIDENCE THAT IT WAS CALLED
    return Inertia::render('Centres/Centres', [
        'type' => 'text',
        'filters' => $filters,
        'centres' => $centres,
        'distances' => [],
    ]);
}

After hours of testing, I realised that the return Inertia::render() call had to come from the same function as the one called by the router - then it works as expected. The search_text() function perhaps doesn't have access to some part of the Inertia workings?

Solution:

The following works, by calling return Inertia::render from within the same controller method - search() - that the router called:

   public function search(Request $request, string $type)
    {
        $search = $request->input('search');

        $filters = new \stdClass();
        $filters->search = $search;

        $centres = Centre::query()
        ->when($filters->search, function ($query, $search) {
            $query->where('name', 'like', '%' . $search . '%')
            ->OrWhere('address', 'like', '%' . $search . '%')
            ->OrWhere('email', 'like', '%' . $search . '%')
            ->OrWhere('notes', 'like', '%' . $search . '%');
        })
        ->with('resources')
        ->paginate($this->pagination);

        // THIS NOW WORKS AND THE COMPONENT IS UPDATED CORRECTLY
        return Inertia::render('Centres/Centres', [
            'type' => 'text',
            'filters' => $filters,
            'centres' => $centres,
            'distances' => [],
        ]);
    }

FULL CODE

Full component code in case useful ... note that the Intertia.post() call came from Search.vue, a subcomponent of Centres.vue

// Centre/Centre.vue
<template>
    <AppLayout title="Centres">
        <template #header>
            <h2 class="font-semibold text-xl text-gray-800 leading-tight">
                Centres
            </h2>
        </template>
        <div class="max-w-7xl mx-auto py-10 sm:px-6 lg:px-8">
            <h2 class="text-xl font-bold mb-5">Find centres</h2>


            <Search :type="type" :filters="filters" />

            <div class="flex gap-4 mt-3">
                <div>
                    <input type="radio" id="location" value="location" v-model="type" />
                    <label class="ml-2" for="location">Location</label>
                </div>
                <div>
                    <input type="radio" id="text" value="text" v-model="type" />
                    <label class="ml-2" for="text">Text</label>
                </div>
            </div>
        </div>

        {{centres}}

        <!-- <Map :centres="centres" /> -->

        <!-- <List :centres="centres" :filters="filters" :distances="distances"/> -->

    </AppLayout>
</template>

<script setup>
    import { ref, reactive, watch } from 'vue';
    import AppLayout from '@/Layouts/AppLayout.vue';
    import { Inertia } from "@inertiajs/inertia";
    import Search from '@/Components/Centres/Search.vue';
    import Map from '@/Components/Centres/Map.vue';
    import List from '@/Components/Centres/List.vue';

    const props = defineProps({
        type: {
            type: String,
            default: 'location',
        },
        filters: {
            type: Object,
            default: () => ({}),
        },
        centres: {
            type: Object,
            default: () => ({}),
        },
        distances: {
            type: Array,
            default: []
        },
    });

    const type = ref(props.type);

</script>
// Search.vue
<template>
    <div>
        <p>Filters: {{ filters }}</p>
        <input v-model="search" type="text" :placeholder="'Search ' + type + '...'"/>
    </div>
</template>

<script>
    import { Inertia } from "@inertiajs/inertia";
    import { ref } from "vue";
    import { watch } from "vue";

    export default {
        components: {
        },
        props: {
            type: {
                type: String,
                default: '',
            },
            filters: {
                type: Object,
                default: () => ({}),
            },
        },
        setup (props) {
            let search = ref(props.filters.search);
            watch(search, (value) => {
                Inertia.post(
                    `/centres/search/${props.type}`,
                    { search: value },
                    {
                        preserveScroll: true,
                    }
                );
            });

            return {
                search
            }
        }
    }
</script>

narkan avatar Sep 02 '22 13:09 narkan

Hey @narkan,

the problem is that you are not returning from your controller method, so the Inertia response is never returned.

// CentresController.php

// THE METHOD CALLED BY THE ROUTER:
public function search(Request $request, string $type)
{
    $search = $request->input('search');

    $filters = new \stdClass();
    $filters->search = $search;

    /*************************** 
     * Here you need to return  
     ***************************/
    return $this->search_text( $filters );
}

// THIS METHOD DOES NOT SEEM TO BE ABLE TO CALL inertia::render()
public function search_text($filters)
{
    $centres = Centre::query()
        ->when($filters->search, function ($query, $search) {
            $query->where('name', 'like', '%' . $search . '%')
            ->OrWhere('address', 'like', '%' . $search . '%')
            ->OrWhere('email', 'like', '%' . $search . '%')
            ->OrWhere('notes', 'like', '%' . $search . '%');
        })
        ->with('resources')
        ->paginate($this->pagination);


        // dd($centres);   >>>   outputs the correct collection as expected


    // THIS SEEMS TO FAIL, BUT NO ERROR MESSAGES OR EVIDENCE THAT IT WAS CALLED
    return Inertia::render('Centres/Centres', [
        'type' => 'text',
        'filters' => $filters,
        'centres' => $centres,
        'distances' => [],
    ]);
}

maxeckel avatar Sep 05 '22 09:09 maxeckel

Hey! Thanks so much for your interest in Inertia.js and for sharing this issue/suggestion.

In an attempt to get on top of the issues and pull requests on this project I am going through all the older issues and PRs and closing them, as there's a decent chance that they have since been resolved or are simply not relevant any longer. My hope is that with a "clean slate" me and the other project maintainers will be able to better keep on top of issues and PRs moving forward.

Of course there's a chance that this issue is still relevant, and if that's the case feel free to simply submit a new issue. The only thing I ask is that you please include a super minimal reproduction of the issue as a Git repo. This makes it much easier for us to reproduce things on our end and ultimately fix it.

Really not trying to be dismissive here, I just need to find a way to get this project back into a state that I am able to maintain it. Hope that makes sense! ❤️

reinink avatar Jul 28 '23 01:07 reinink