I have two a view that loads a livewire component
<x-layouts.formularies>
<livewire:formularies.edit.display-section :section="$section->id" :formulary="$formulary->id" />
</x-layouts.formularies>
This component (display-section
) calls a child component in a loop...
<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>
Now, this is the display-row
component:
<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>
When the user picks an option from the radio button, it does some database stuff and fires an event that should be listened by the parent component (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' );
}
As you can see, this function emits a formularyUpdated
event with the id of the formulary. And in the DisplaySection I have this:
protected $listeners = [
'sectionAdvanced',
'formularyUpdated',
];
/**
*
*/
public function formularyUpdated( Formulary $formulary )
{
dd( 'This dd should stop the execution' );
}
For some reason, this always ends with "I should not get here if there is a dd before"
so, what should I change? why is my listener not listening or not being fired? I have tried different approaches without luck.
CodePudding user response:
The short answer
To briefly summarize this; the event is emitted in the component (PHP) - which event(s) are emitted and which data that goes with it, is sent via the response. Livewire handles the response, and checks if any events where emitted - if yes, it tells the JavaScript objects corresponding with that event, that a such event was emitted - and these JavaScript objects will emit their own network request to process the event.
In other words, the entire listening and emitting for events actually happens in JavaScript, in the response and requests that Livewire makes back and forth between the server.
The longer answer
When Livewire processes the request, it will figure out which methods to call and which properties to set. It will see that you are calling a method, and calling that method emits the event through $this->emit()
, which in your case is the method setAnswer
. After doing this, Livewire will in the response add that you emitted an event. The listener for these events are in JavaScript - as it does not communicate directly with the other PHP components.
So, your request fires, you emit the event, and the response returns something that indicates that an event was emitted. If we have a look at the network-response from that request, it might look something like this (this is pseudo-code/test-data from my local environment),
As you can see, it has one emitted event. We can emit more events, and they will appear in the order of which they were emitted.
Same thing goes for dispatched browser-events, they will appear here as well,
For the things I've shown here, this is the method that is being called from a 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!');
}
So when Livewire then receives the response, it checks if it has some effects that it needs to process - if there are any dispatched browser-events, emitted Livewire-events or anything else.
If the response carries data under effects.emits
, it will emit those events, similar to using Livewire.emit('event-name', ...data)
in native JavaScript, and the JavaScript objects that corresponds with the different components, will pick up those and fire a new request to the corresponding component. Let's expand our example,
protected $listeners = ['myListener'];
public function myMethod()
{
$this->emit('myListener', 'My first emit data');
}
public function myListener($data = null)
{
// Do something with $data
}
Now when we call the method myMethod
, a network request is fired, as expected and as we saw before. But now we also get a second request, which is the emitted event that we listened for before. Again we can use the network-inspector, and this time we're interested in the request and not the response.
As we can see, there are two requests - the first one is the request which emits the event, the second is the one that listened for that event. The second request has a update
parameter, which has a type of fireEvent
, it also has some other Livewire-data, and the parameters which is the data. This is where the magic happens, and how it listenes those events.