slydog
slydog

Reputation: 1

Why do flash messages in Laravel Livewire only show once?

I have a problem with flash messages not showing when using Laravel Livewire.

If I use just wire:model and then click the save button everything works fine and the success message is displayed. But if I use wire:model.defer the flash message only works once after the page has been refreshed, so for example, the page loads, I change a form field and click save everything works fine. But, let's say I forgot to change a field and now click save, the flash message doesn't show, only if I reload the page.

This person appears to have the same issue but no solutions offered. same problem?

<?php

namespace App\Http\Livewire;
use App\Models\User;
use Illuminate\Support\Facades\Auth;

use Livewire\Component;

class ProfileSettings extends Component
{
    public $display_name;
    public $bio;
    public $location;
    public $website_url;
    public $subscription;
    public $hide_status;
    public $hide_profile;


    public function UpdateProfile()
    {
        $validatedData = $this->validate(
            [
                'display_name' => ['required', 'string', 'min:3', 'max:40', 'regex:/^[a-zA-Z0-9.\s]+$/'],
                'bio' => ['nullable', 'string', 'max:1000'],
                'location' => ['nullable', 'string', 'max:100'],
                'website_url' => ['nullable', 'max:100', 'url'],
                'subscription' => ['nullable', 'numeric', 'min:4.99', 'max:29.99'],
                'hide_status' => 'boolean',
                'hide_profile' => 'boolean', 
            ]
        );

        // Store subscription price in cents.
        if($validatedData['subscription'])
            $validatedData['subscription'] = $this->subscription * 100;
        else
            $validatedData['subscription'] = NULL; 
  
        User::where('id', auth::user()->id)->update($validatedData);

        session()->flash('message', 'Profile settings successfully updated!');
    }
    

    // Populate the form fields with stored data.
    public function mount() {
        $this->display_name = Auth::user()->display_name;
        $this->bio = Auth::user()->bio;
        $this->location = Auth::user()->location;
        $this->website_url = Auth::user()->website_url;
        
        // If a subscription price is set, convert from cents to dollars
        // with two decimal places for displaying.
        if (Auth::user()->subscription)
        {
            $dollars = Auth::user()->subscription / 100;
            $this->subscription = number_format($dollars, 2, '.', ',');
        }

        $this->hide_status = Auth::user()->hide_status;
        $this->hide_profile = Auth::user()->hide_profile;
    }


    public function render()
    {   
        
        return view('livewire.profile-settings');
    }
}
    <div>
        <h3 class="text-xl mb-2">Profile</h3>
        <p>Here you can edit your profile information, privacy and subscription details.</p>
    </div>
    <div class="bg-white rounded-md lg:shadow-md shadow col-span-2">
        <form wire:submit.prevent="UpdateProfile">
            <div class="grid grid-cols-1 gap-3 lg:p-6 p-4">

                <div>
                    <label for="display_name">Display Name</label>
                    <input id="display_name" type="text" class="shadow-none with-border focus:ring-0" wire:model.defer="display_name">
                    @error('display_name')<x-validation-errors :message="$message"/>@enderror
                </div>
                <div>
                    <label for="bio">Bio</label>  
                    <textarea id="bio" rows="3" class="shadow-none bg-gray-100 with-border focus:ring-0" wire:model.defer="bio"></textarea>
                    @error('bio')<x-validation-errors :message="$message"/>@enderror
                </div>
                <div>
                    <label for="location">Location</label>
                    <input id="location" type="text" class="shadow-none with-border focus:ring-0" wire:model.defer="location">
                    @error('location')<x-validation-errors :message="$message"/>@enderror
                </div>
                <div>
                    <label for="website_url">Website URL</label>
                    <input id="website_url" type="text" class="shadow-none with-border focus:ring-0" wire:model.defer="website_url">
                    @error('website_url')<x-validation-errors :message="$message"/>@enderror
                </div>
                <div>
                    <label for="subscription">Subscription Price - minimum $4.99 USD per month or leave blank for free.</label>
                    <div class="flex items-center">
                        <div class="-mr-1 bg-gray-100 border px-3 py-3 rounded-l-md">$</div>
                        <input id="subscription" type="text" class="shadow-none with-border focus:ring-0" wire:model.defer="subscription">
                    </div>
                        @error('subscription')<x-validation-errors :message="$message"/>@enderror
                </div>
                <div class="mt-5 space-y-5 ">
                    <div class="flex justify-between items-center">
                        <div>
                            <h4>Activity Status</h4>
                            <div>Enable to hide your last seen status on your profile.</div>
                        </div>
                        <div class="switches-list -mt-8 is-large">
                            <div class="switch-container">
                                <input type="hidden" value="0" wire:mode.defer="hide_status">
                                <label class="switch"><input type="checkbox" value="1" wire:model.defer="hide_status"><span class="switch-button"></span></label>
                            </div>
                        </div>
                    </div>
                    <hr>
                    <div class="flex justify-between items-center">
                        <div>
                            <h4>Hide Profile</h4>
                            <div>Enable to hide your profile from search results.</div>
                        </div>
                        <div class="switches-list -mt-8 is-large">
                            <div class="switch-container">
                                <input type="hidden" value="0"wire:mode.defer="hide_profile" >
                                <label class="switch"><input type="checkbox" value="1" wire:model.defer="hide_profile"><span class="switch-button"></span></label>
                            </div>
                        </div>
                    </div>
                </div>
               
                <div>
                <x-auth-session-status class="mb-4" :status="session('message')"/>
                </div>

                <div class="flex items-center justify-center">
                    <x-button>
                        <span wire:loading.delay wire:target="UpdateProfile"><x-loading /></span>
                        <span wire:loading.remove wire:target="UpdateProfile">{{ __('Save') }}</span>
                    </x-button>
                </div>
    
            </div>
        </form>

    </div>
</div>

@if ($status)
<div x-data="{ show: true }" x-show="show"
    class="flex justify-between items-center bg-green-200 relative py-3 px-3 rounded-lg">
    <div>
        <div class="ml-2 text-sm text-green-600">
        {{ $status }}
        </div>
    </div>
    <div>
        <button type="button" @click="show = false" class=" text-green-700">
            <span class="text-2xl">&times;</span>
        </button>
    </div>
</div>
@endif

Upvotes: 0

Views: 3919

Answers (2)

masood hoseiny
masood hoseiny

Reputation: 1

<form wire:submit.prevent="update">
<div>
    @if (session()->has('message'))
        <div class="alert alert-success">
            {{ session('message') }}
        </div>
    @endif
</div>

Title: <input wire:model="post.title" type="text">

<button>Save</button>

in your blade insert div

Upvotes: 0

Peppermintology
Peppermintology

Reputation: 10210

I think this has less to do with Livewire and more to do with Alpine and it's lifecycle. The reason it works the first time is because the Alpine component is not in the DOM and so when the flash message is added to the DOM, it is initialized with x-data="{ show: true } so your component is displayed. When you click the close button to change the value of show to false, then update something and press save, the component is already in the DOM and so not initialized again meaning the value of show is not set to true.

There are examples of using flash messages with Livewire and Alpine in the Livewaire stack of Jetstream. It uses events rather than session().

public function save()
{
    // ...
    $this->emit('saved');
}
<div x-data="{ shown: false, timeout: null }"
    x-init="@this.on('saved', () => { clearTimeout(timeout); shown = true; timeout = setTimeout(() => { shown = false }, 2000); })"
    x-show.transition.opacity.out.duration.1500ms="shown"
    style="display: none;">
    {{ __('Updated.') }}
</div>

This initializes the Alpine component and attaches a listener for the saved event so that any time it hears the saved event it triggers a function to execute which shows the message and then clears.

You could remove the setTimeout stuff and just have it set shown = true if you wanted to allow users to manually clear the message.

Upvotes: 4

Related Questions