bwrigley's avatar

Struggling to get flash messages to show in Vue component

Hi There,

I'm brand new to Vue and Intertia, but I'm trying to convert an existing Laravel project into a Laravel/Vue project as a way to put the Intertai/Vue tutorials into action.

I am trying to build on how flash messages are shown in the Intertia documentation but clearly I am doing something wrong, and probably overcomplicating things.

I have a FlashMessage component:

<script setup>
    import { usePage } from '@inertiajs/vue3';
    import { computed } from '@vue/reactivity';
    import { onMounted, ref } from 'vue';

   let show = ref(true);

    const props = defineProps({
            error : {
                type : String,
                default: 'bg-red-800'
            },
            success : {
                type : String,
                default : 'bg-green-800'
            },
            notify : {
                type  :String,
                default : 'bg-blue-500'
            },
            class : {
                type : String,
                default : ''
            }
    });

    let flash = computed(() => {

        if (usePage().props.flash.error) {
            return {
                message : usePage().props.flash.error,
                class : props.class + ' ' + props.error
            }
        }
        else if ( usePage().props.flash.success){
            return {
                message : usePage().props.flash.success,
                class : props.class + ' ' + props.success
            }
        }
        else {
            return {
                message : usePage().props.flash.notify,
                class : props.class + ' ' + props.notify
            }
        }
    });

    onMounted(() => {
        setTimeout(() => {
            show.value = false
        },5000);
    });

</script>



<template>
    <div
        v-show="show"
        class="text-white fixed bottom-2 lg:bottom-10 right-2 lg:right-10 p-2 lg:p-5 rounded-2xl"
        :class="flash.class"
    >
        {{  flash.message }}

    </div>
</template>

which is included in my global layout:

<script setup>

import { Head } from '@inertiajs/vue3';

import Navbar from './Navigation/Navbar.vue';
import FlashMessage from './FlashMessage.vue';

const props = defineProps({
    title: {
      type : String,
      default : null
    }

});

</script>

<template>
    <div class="bottom-0 fixed">*vue</div>

    <Head :title="title ? $page.props.appName + ' - ' + title : $page.props.appName" />

    <Navbar v-if="$page.props.auth.user"/>

    <main class="text-2xl grid place-items-center h-screen bg-gray-900">

      <slot />

    </main>
    <div v-if="!Object.values($page.props.flash).every(v => v===null)">
        <FlashMessage/>
    </div>

</template>

and I have added the relevant lines to the HandInterRequests middleware (correctly I hope)

    public function share(Request $request): array
    {
        return array_merge(parent::share($request), [
            'appName' => config('app.name'),

            'auth.user' => fn () => $request->user()
                ? $request->user()->only('id', 'name', 'email','streak')
                : null,

            'flash' => [
                'success' => fn () => $request->session()->get('success'),
                'error' => fn () => $request->session()->get('error'),
                'notify' => fn () => $request->session()->get('notify')
            ],
        ]);
    }

Then I have a SessionsController where I handle authentication - this was a custom build, not using Breeze or any of the other scaffoldings. I have a store method where I authenticate a user and then forward them to a dashboard, but I want to include a flash message and I'm not sure how:

    public function store()
    {

        $attributes = request()->validate([
            'email' => ['required','email'],
            'password' => ['required']
        ]);

        if (auth()->attempt($attributes)){

            $user = auth()->user();

            if ($user->mostRecentTest()  && ! (Carbon::parse($user->mostRecentTest()->completed_at)->isToday() ||  Carbon::parse($user->mostRecentTest()->completed_at)->isYesterday())){
                $user->streak = 0;
                $user->save();
            }

            return Redirect::route('topic.index')->with('success', 'Welcome back ' . $user->name);

        }

        return back()->withErrors(['email' => 'Your provided credentials cannot be verified.']);

    }

And lastly my TopicController ```index```` method that shows the dashboard:

    public function index()
    {
        return Inertia::render('Topic/Index',[
            'topics' => Auth::check() ? Topic::mine()->orderBy('name')->get() : null
        ]);

    }

This successfully redirects the user to the dashboard but no flash message is shown and using the Vue.js devtools, when I examine the Layout component, all three flash properties are null.

{"success":null,"error":null,"notify":null}

So either I am not setting the flash message or its getting lost in the redirect, or I'm doing something else wrong!

Thanks for any help.

0 likes
1 reply
bwrigley's avatar

I think I have figured out the problem (or part of it), but still not the solution.

When I have a method that does a double redirect I lose the flash message from the session.

Say I have a store method in my SessionController which authenticates a user to and redirects them to my named 'home' route like so:

	return Redirect::route('home')->with('success', 'Welcome back ' . $user->name);

which routes like this:

		Route::get('/', [HomeController::class, 'show'])->name('home');

the show method in HomeController performs some logic and then redirects the user again like so:

		return redirect(route('topics.home'));

in this instance the flash messages in the session are null.

However if in my SessionsController store method I redirect straight to topics.home I do not lose the flash message.

My guess is that this is the double redirect. Is there a way to manage that?

Please or to participate in this conversation.