Laravel-Livewire 如何触发事件并监听它们?

Posted

技术标签:

【中文标题】Laravel-Livewire 如何触发事件并监听它们?【英文标题】:How does Laravel-Livewire fires events and listens to them? 【发布时间】:2021-12-05 13:18:22 【问题描述】:

我有两个加载 livewire 组件的视图

<x-layouts.formularies>
    <livewire:formularies.edit.display-section :section="$section->id" :formulary="$formulary->id" />
</x-layouts.formularies>

这个组件(display-section)在循环中调用一个子组件...

<div class="letter mx-auto" id="section-holder">
    <div class="paper-title d-flex justify-content-between">
        @foreach($questionnaire->getData( 'logos', [] ) as $logo)
            <img src=" asset($logo[ 'path' ]) " class="img-fluid h-100" />
        @endforeach
    </div>

    <div class="section-questions">
        @foreach($section->questions as $question)
            <livewire:formularies.edit.display-row :question="$question->id"
                                                    :formulary="$formulary->id" :wire:key="$question->id" />
            <hr />
        @endforeach
    </div>
</div>

现在,这是 display-row 组件:

<div class="card-body text-center p-1">
    <div class="btn-group m-1" role="group" aria-label="question- $question->id ">
        @foreach($question->answers as $answer)
            <input type="radio"
                    wire:model="answer"
                    :wire:key="$question->id . '-' . $answer->id"
                    wire:click="setAnswer"
                    class="btn-check"
                    value=" $answer->id "
                    name="question- $question->id "
                    id="question- $question->id - $answer->id "
                    autocomplete="off" />
            <label class="btn btn-outline-teal text-dark mb-0"
                    for="question- $question->id - $answer->id "> $answer->statement </label>
        @endforeach
    </div>
</div>

当用户从单选按钮中选择一个选项时,它会执行一些数据库操作并触发应该由父组件 (display-section) 监听的事件:

public function setAnswer()

    $this->emit( 'formularyUpdated', $this->formulary->id );
    FormularyUpdated::dispatch( $this->formulary, $this->question, $this->answer );
    dd( 'I should not get here if there is a dd before' );

如您所见,此函数会发出一个带有公式 ID 的 formularyUpdated 事件。在 DisplaySection 我有这个:

protected $listeners = [
    'sectionAdvanced',
    'formularyUpdated',
];

/**
    *
    */
public function formularyUpdated( Formulary $formulary )

    dd( 'This dd should stop the execution' );

由于某种原因,这总是以 "I should not get here if there is a dd before" 结尾,那么,我应该改变什么?为什么我的听众不听或不被解雇?我尝试了不同的方法,但没有运气。

【问题讨论】:

【参考方案1】:

简短的回答

简要总结一下;事件在组件 (php) 中发出 - 发出哪些事件以及随之而来的数据是通过响应发送的。 Livewire 处理响应,并检查是否发出了任何事件——如果是,它告诉与该事件对应的 javascript 对象,发出了这样的事件——这些 JavaScript 对象将发出自己的网络请求来处理该事件。

换句话说,整个事件的侦听和发送实际上发生在 JavaScript 中,发生在 Livewire 在服务器之间来回发出的响应和请求中。


更长的答案

当 Livewire 处理请求时,它会确定要调用哪些方法以及要设置哪些属性。它将看到您正在调用一个方法,并且调用该方法会通过$this-&gt;emit() 发出事件,在您的情况下是方法setAnswer。完成此操作后,Livewire 将在响应中添加您发出的事件。这些事件的侦听器在 JavaScript 中 - 因为它不直接与其他 PHP 组件通信。

因此,您的请求触发,您发出事件,响应返回的内容表明事件已发出。如果我们查看来自该请求的网络响应,它可能看起来像这样(这是来自我本地环境的伪代码/测试数据),

如您所见,它有一个发出的事件。我们可以发出更多的事件,它们会按照发出的顺序出现。

分派的浏览器事件也是如此,它们也会出现在这里,

对于我在这里展示的内容,这是从 wire:click 调用的方法,

public function myMethod()

    $this->emit('my-first-emit', 'My first emit data');
    $this->emit('my-second-emit', ['This time', 'the emit', 'passes an', 'array']);
    $this->dispatchBrowserEvent('browser-dispatched-event', 'My browser-event data!');

因此,当 Livewire 收到响应后,它会检查它是否有一些需要处理的效果 - 是否有任何已调度的浏览器事件、发出的 Livewire 事件或其他任何东西。

如果响应携带 effects.emits 下的数据,它将发出这些事件,类似于在原生 JavaScript 中使用 Livewire.emit('event-name', ...data),并且与不同组件对应的 JavaScript 对象将拾取这些事件并触发新的请求对应的组件。让我们扩展我们的例子,

protected $listeners = ['myListener'];

public function myMethod()

    $this->emit('myListener', 'My first emit data');


public function myListener($data = null)

    // Do something with $data

现在,当我们调用 myMethod 方法时,会触发一个网络请求,正如我们之前看到的那样。但是现在我们也得到了第二个请求,这是我们之前监听的发出的事件。我们可以再次使用网络检查器,这次我们对请求而不是响应感兴趣。

正如我们所见,有两个请求 - 第一个是发出事件的请求,第二个是侦听该事件的请求。第二个请求有一个update参数,它的类型是fireEvent,它还有一些其他的Livewire-data,参数是数据。这就是魔法发生的地方,也是它监听这些事件的方式。

【讨论】:

所以,基本上,emmited 事件只有在所有 php 完成后才会执行,这就是为什么我的“这个 dd 应该停止执行”从未见过的原因。感谢您的解释,可悲的是,我之前仍然需要一种方法来达到该声明,我猜测 laravel 事件 FormularyUpdated::dispatch( $this->formulary, $this->question, $this->answer );是能够实现它的人 是的,没错——如果你使用 Laravel 事件,它可以在响应之前在 PHP 中处理它。 Livewire 发出的事件只会在请求完成并有响应后触发。这样做的原因是 Livewire 不一定知道 PHP 中该页面上存在哪些其他组件,因为它们都是隔离的。由于您在其中有一个dd(),它永远不会返回正确的 HTTP 响应,这就是该事件实际上没有被触发的原因。

以上是关于Laravel-Livewire 如何触发事件并监听它们?的主要内容,如果未能解决你的问题,请参考以下文章

如何找到在 BigQuery 中触发设置事件之前触发的事件?

如何触发下拉框下拉事件

页面按钮如何触发声音事件?

回车事件如何触发不同的click

js如何在页面滚动到一定位置时触发事件?

js如何触发页面按回车的事件