You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Livewire 3表单提交后触发验证码模态窗的问题及报错解决

表单提交后无法正常弹出验证码模态窗(含Bootstrap报错)

我在实现表单提交后显示模态窗时遇到问题:预订记录写入数据库后,需要立即弹出让用户输入验证码的模态窗,但功能无法实现。更新代码后出现如下报错:

Uncaught TypeError: Cannot read properties of undefined (reading 'backdrop')
    at On._initializeBackDrop (modal.js:158:39)
    at new On (modal.js:69:27)
    at On.getOrCreateInstance (base-component.js:65:41)
    at HTMLButtonElement.<anonymous> (modal.js:363:22)
    at HTMLDocument.n (event-handler.js:118:19)

相关代码

Booking组件代码

<?php

namespace App\Livewire;

use Livewire\Component;
use App\Models\Reservation;
use App\Mail\ReservationMail;
use Illuminate\Support\Facades\Mail;
use App\Models\AvailableBusinessHour;
use App\Events\ShowVerificationModal; // Přidejte tuto řádku pro event

class Booking extends Component
{
    public $full_name;
    public $email;
    public $phone_number;
    public $reservation;

    public $selectedService;
    public $available_business_hour_id;
    public $selectedBusinessHourId;
    public $verification_code;
    public $successMessage;

    public $showVerificationModal = false;
    public $successMessageVisible;
    public $successMessageTimeout = 3000;

    public function render()
    {
        $this->available_business_hour_id = AvailableBusinessHour::whereDoesntHave('reservation')->orderBy('day')->orderBy('from')->get();
        return view('livewire.booking', [
            'emptyOption' => 'Vyber si čas rezervace',
        ]);
    }

    public function store()
{
    $this->validate([
        'full_name' => 'required|string|max:250',
        'email' => 'required|email|max:250',
        'phone_number' => 'required|phone:CZ',
        'selectedService' => 'required',
        'available_business_hour_id' => 'required|unique:reservations',
    ], [
        'available_business_hour_id.unique' => 'Tento termín byl již obsazen, vyber jiný pokud je nějaký dostupný.',
        'available_business_hour_id.required' => 'Výběr času objednání je povinné pole.',
        'phone_number.phone' => 'Chybný formát telefonního čísla.',
        'selectedService.required' => 'Toto je povinné pole',
    ]);

    $selectedBusinessHour = AvailableBusinessHour::find($this->selectedBusinessHourId);

    $verification_code = rand(100000, 999999);

    $reservation = Reservation::create([
        'full_name' => $this->full_name,
        'email' => $this->email,
        'phone_number' => $this->phone_number,
        'service' => $this->selectedService,
        'available_business_hour_id' => $this->selectedBusinessHourId,
        'day' => $selectedBusinessHour['day'],
        'from' => $selectedBusinessHour['from'],
        'to' => $selectedBusinessHour['to'],
        'verification_code' => $verification_code
    ]);

    $this->sendReservationConfirmationMail($reservation);

    $this->successMessage = 'Rezervace proběhla úspěšně.';
    $this->successMessageVisible = true;

    $this->showVerificationModal = true;

    $this->reset(['full_name', 'email', 'phone_number', 'available_business_hour_id']);
}

    public function sendReservationConfirmationMail($reservation)
    {
        Mail::to($reservation->email)->send(new ReservationMail($reservation));
    }

    public function services()
    {
        return ["Stříhání vlasů", "Stříhání + úprava vousů", "Barvení vousů", "Junior střih", "Úprava vousů"];
    }
}

Booking组件对应的Blade视图

<section id="booking">
    <div class="container">
        @if ($successMessageVisible)
            <div x-data="{ show: true }" x-init="setTimeout(() => show = false, {{ $successMessageTimeout }})" x-show="show" class="alert alert-success">
                {{ $successMessage }}
            </div>
        @endif
        <form wire:submit.prevent="store">
            @csrf
            <div class="col-12 mb-4">
                <h1 class="modal-title fs-5 text-center" id="bookingModalLabel">Objednej se</h1>
                <p class="">Lorem, ipsum dolor sit amet consectetur adipisicing elit. Lorem, ipsum
                    dolor sit amet consectetur adipisicing elit.</p>
            </div>
            <div class="form-group col-12">
                <input type="text" wire:model="full_name" id="full_name" name="full_name" class="form-control"
                    placeholder="Celé jméno*" required>
            </div>
            <div class="form-group col-12">
                <input type="email" wire:model="email" id="email" name="email" class="form-control"
                    placeholder="Email*" required>
            </div>
            <div class="form-group col-12">
                <input type="text" wire:model="phone_number" id="phone_number" name="phone_number"
                    class="form-control" placeholder="Tel. číslo ve formátu +420700700700" required>
                @error('phone_number')
                    <span class="text-danger">{{ $message }}</span>
                @enderror
            </div>
            <div class="form-group col-12">
                <select wire:model="selectedService" class="form-control">
                    <option value="">Vyberte službu</option>
                    @foreach ($this->services() as $service)
                        <option value="{{ $service }}">{{ $service }}</option>
                    @endforeach
                </select>
                @error('selectedService')
                    <span class="text-danger">{{ $message }}</span>
                @enderror
            </div>

            <div class="form-group col-12 mt-3">
                <select wire:model="selectedBusinessHourId" class="form-control">
                    <option value="">{{ $emptyOption }}</option>
                    @foreach ($available_business_hour_id as $time)
                        <option value="{{ $time['id'] }}">{{ $time['formattedDayAndDate'] }} od {{ $time['from'] }} do
                            {{ $time['to'] }}</option>
                    @endforeach
                </select>
                @error('available_business_hour_id')
                    <span class="text-danger">{{ $message }}</span>
                @enderror
            </div>
            <div class="form-group col-12 text-center">
                <div class="cta-btns">
                    <button type="submit" class="btn btn-service me-sm-2"  data-bs-toggle="modal"
                        data-bs-target="#exampleModal">Odeslat</button>
                        @if ($showVerificationModal)
                            <livewire:verification-modal />
                        @endif
                </div>
            </div>
        </form>
    </div>
</section>

VerificationModal组件代码

<?php

namespace App\Livewire;

use Livewire\Component;

class VerificationModal extends Component
{
    protected $listeners = ['showVerificationModal'];

    public function render()
    {
        return view('livewire.verification-modal');
    }
}

VerificationModal组件对应的Blade视图

<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <h1 class="modal-title fs-5" id="exampleModalLabel">Modal title</h1>
          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
        </div>
        <div class="modal-body">
            <div class="verification-code-container">
                {{-- <input type="text" wire:model="verification_code" id="verification_code" name="verification_code" class="form-control" placeholder="Verifikační kód" required> --}}
            </div>
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
          <button type="button" class="btn btn-primary">Save changes</button>
        </div>
      </div>
    </div>
  </div>

解决方案

问题根源

  1. 报错是因为提交按钮自带Bootstrap模态触发属性,点击时会直接尝试打开模态窗,但此时模态窗组件还未渲染(受@if($showVerificationModal)条件限制),导致Bootstrap找不到DOM元素抛出错误。
  2. 表单验证规则中字段名不匹配,available_business_hour_id对应的数据实际是selectedBusinessHourId,会导致验证失效。
  3. 模态窗的显示逻辑依赖组件条件渲染,无法实时响应Livewire状态变化。

修复步骤

1. 移除提交按钮的Bootstrap触发属性

修改Booking视图中的提交按钮,去掉data-bs-toggle和data-bs-target:

<button type="submit" class="btn btn-service me-sm-2">Odeslat</button>

2. 调整模态窗渲染位置

把livewire:verification-modal移出条件判断,放在表单外部确保DOM始终存在:

</form>
<livewire:verification-modal />

3. 通过Livewire事件控制模态窗显示

修改Booking组件的store方法,用事件触发替代直接赋值:

// 替换$this->showVerificationModal = true;
$this->dispatch('show-verification-modal');

更新VerificationModal组件,监听事件并调用Bootstrap API:

namespace App\Livewire;

use Livewire\Component;

class VerificationModal extends Component
{
    protected $listeners = ['show-verification-modal' => 'openModal'];

    public function openModal()
    {
        $this->dispatch('trigger-modal');
    }

    public function render()
    {
        return view('livewire.verification-modal');
    }
}

修改VerificationModal的Blade视图,用Alpine.js监听事件并打开模态窗:

<div 
    class="modal fade" 
    id="exampleModal" 
    tabindex="-1" 
    aria-labelledby="exampleModalLabel" 
    aria-hidden="true"
    x-data
    x-on:trigger-modal.window="new bootstrap.Modal(document.getElementById('exampleModal')).show()"
>
    <!-- 原有模态窗内容不变 -->
</div>

4. 修正表单验证字段

把store方法中的验证规则字段名改为selectedBusinessHourId:

$this->validate([
    // ...其他规则
    'selectedBusinessHourId' => 'required|unique:reservations,available_business_hour_id',
], [
    'selectedBusinessHourId.unique' => 'Tento termín byl již obsazen, vyber jiný pokud je nějaký dostupný.',
    'selectedBusinessHourId.required' => 'Výběr času objednání je povinné pole.',
    // ...其他错误信息
]);

5. 完善表单重置逻辑

确保所有表单字段都被重置:

$this->reset(['full_name', 'email', 'phone_number', 'selectedService', 'selectedBusinessHourId']);

修复后流程

用户提交表单→验证通过创建预订记录→触发Livewire事件→VerificationModal组件收到事件后调用Bootstrap API打开模态窗→用户输入验证码完成验证。

内容的提问来源于stack exchange,提问作者user22809416

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 11:44:57