为什么Laravel Livewire中使用wire:model.defer时闪存消息仅显示一次
问题根因
- 你遇到的现象是Livewire的请求优化机制导致的:
wire:model.defer会将字段变更攒到提交时统一发送,如果没有任何表单字段修改,Livewire会判定没有状态变更,要么跳过服务端请求,要么就算请求执行完成,也会因为检测不到公共属性变化而不触发DOM更新;而session闪存属于单次请求有效,只有触发DOM重渲染才会读取展示,所以就出现了不修改字段就不显示提示的问题。 - 额外修复:你代码里两个隐藏域的指令拼写错误,
wire:mode.defer需要修正为wire:model.defer,否则会导致布尔类型字段提交异常。
解决方案(优先推荐)
直接放弃使用session闪存存储提示消息,改用Livewire组件公共属性存储,完全避开和请求优化逻辑的冲突:
1. 修改Livewire组件代码
<?php namespace App\Http\Livewire; use App\Models\User; use Illuminate\Support\Facades\Auth; use Livewire\Component; class ProfileSettings extends Component { // 新增公共属性存储提示消息 public $message; 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,直接给公共属性赋值 $this->message = '个人资料设置更新成功!'; } // 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'); } }
2. 修改表单视图代码
将原来读取session的提示组件改为读取组件公共属性:
<div> <h3 class="text-xl mb-2">个人资料</h3> <p>你可以在此修改个人信息、隐私设置和订阅相关配置。</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">显示名称</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">个人简介</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">所在地</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">个人网站</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">订阅价格 - 最低4.99美元/月,留空则设为免费</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>在线状态</h4> <div>开启后将在个人资料页隐藏你的最后活跃时间</div> </div> <div class="switches-list -mt-8 is-large"> <div class="switch-container"> <input type="hidden" value="0" wire:model.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>隐藏个人资料</h4> <div>开启后你的个人资料将不会出现在搜索结果中</div> </div> <div class="switches-list -mt-8 is-large"> <div class="switch-container"> <input type="hidden" value="0" wire:model.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> <!-- 仅修改这一行,把session('message')改为$message --> <x-auth-session-status class="mb-4" :status="$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">{{ __('保存') }}</span> </x-button> </div> </div> </form> </div> </div>
3. 可选优化:给提示组件加自动隐藏逻辑
修改x-auth-session-status组件代码,新增3秒自动消失效果:
@if ($status) <div x-data="{ show: true }" x-show="show" x-init="setTimeout(() => show = false, 3000)" 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">×</span> </button> </div> </div> @endif
内容的提问来源于stack exchange,提问作者slydog
相关产品推荐
相关产品推荐

