使用 属性 alpine js 和 livewire 调度浏览器事件

Dispatching browser event with property alpine js and livewire

我在使用 alpine 和 livewire 在浏览器上发送事件时尝试传递参数

第一次尝试的结果是我所期望的,id 与我通过的 属性 匹配:

但是在第二次尝试时,奇怪的事情发生了,id 与我通过的 属性 不匹配:

我传递参数的 blade 视图 :

<div>
    <div class="card">
        <div class="d-flex justify-content-between align-items-center card-header">
          Todo List
          <livewire:todos.todo-add />
        </div>
        <ul class="list-group list-group-flush">
            @forelse ($todos as $todo)    
                <li class="list-group-item d-flex justify-content-between">
                    {{ $todo->title }}
                    <div x-data>
                        <button
                            class="btn btn-success"
                            @click.prevent="$dispatch('toast-confirmation', {
                                id: {{ $todo->id }},
                                action: 'complete',
                                message: 'Are you sure to complete this task ?'
                            })">Complete</button>
                        <button
                            class="btn btn-danger"
                            @click.prevent="$dispatch('alert-confirmation', {
                                id: {{ $todo->id }},
                                action: 'delete',
                                message: 'Are you sure to delete this task ?'
                            })">Delete</button>
                    </div>
                </li>
            @empty
                No data.
            @endforelse
        </ul>
    </div>
    <div class="d-flex justify-content-end mt-3">
        {{ $todos->links() }}
    </div>

    <x-alerts />
    <x-toasters />
</div>

TodoIndex livewire class :

class TodoIndex extends Component
{
    use WithPagination;

    protected $paginationTheme = 'bootstrap';
    
    protected $listeners = [
        'todoAdded' => '$refresh',
        'complete',
        'delete',
    ];

    public function complete(Todo $todo)
    {
        $todo->update();

        $this->dispatchBrowserEvent('toast', [
            'type' => 'success',
            'message' => 'Task has been completed'
        ]);
    }
    
    public function delete(Todo $todo)
    {
        $todo->delete();

        $this->dispatchBrowserEvent('toast', [
            'type' => 'success',
            'message' => 'Task has been deleted'
        ]);
    }

    public function render()
    {
        return view('livewire.todos.todo-index', [
            'todos' => Todo::latest()->paginate(5)
        ]);
    }
}

警报(laravel-component):

<div>
    @push('alerts')
        <script>
            // Alert Confirmation
            window.addEventListener('alert-confirmation', event => {
                Swal.fire({
                    title: 'Are you sure to delete this id : ' + event.detail.id + '?',
                    text: "You won't be able to revert this!",
                    icon: 'warning',
                    showCancelButton: true,
                    confirmButtonColor: '#3085d6',
                    cancelButtonColor: '#d33',
                    confirmButtonText: 'Yes, delete it!'
                }).then((result) => {
                    if (result.isConfirmed) {
                        Livewire.emit(event.detail.action, event.detail.id)
                    }
                })
            })

            // Alert Info
            window.addEventListener('alert-info', event => {
                Swal.fire({
                    icon: event.detail.icon,
                    title: 'Oops...',
                    text: 'Something went wrong!'
                })
            })
        </script>
    @endpush
</div>

app.layouts(laravel 分量):

<!doctype html>
<html lang="en">
    <head>
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <!-- Bootstrap CSS -->
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">

        <title>Hello, world!</title>

        <link rel="stylesheet" href="/css/style.css">
    
        @livewireStyles
        
        <script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.8.2/dist/alpine.min.js" defer></script>
    </head>
    <body>
        <x-navbar />
        
        <div class="container py-4">
            {{ $slot }}
        </div>


        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
        
        {{-- SweetAlert2 --}}
        <script src="//cdn.jsdelivr.net/npm/sweetalert2@11"></script>

        {{-- Toastr --}}
        <script src="/js/cute-alert/cute-alert.js"></script>
        
        @stack('alerts')

        @stack('toasters')

        @livewireScripts
    </body>
</html>

知道如何解决这个问题吗?

从 discord 得到答案,感谢 Josh Hansley 先生 实际上它在文档中也提到了 Dom Diffing Issue

在我的例子中,解决方案是像这样添加 wire:key="todo-{{ $todo->id }} :

<li class="list-group-item d-flex justify-content-between" wire:key="todo-{{ $todo->id }}">

    {{ $todo->title }}

    <div x-data>

        <button
            class="btn btn-success"
            @click.prevent="$dispatch('toast-confirmation', {
                id: {{ $todo->id }},
                action: 'complete',
                message: 'Are you sure to complete this task ?'
            })">Complete</button>
        <button
            class="btn btn-danger"
            @click.prevent="$dispatch('alert-confirmation', {
                id: {{ $todo->id }},
                action: 'delete',
                message: 'Are you sure to delete this task ?'
            })">Delete</button>

    </div>
</li>

添加“todo-”前缀是最佳做法以避免发生一些奇怪的词态错误