23
Unexpected livewire behavior with global Laravel methods
In todays episode of what I learned this week, I have a fun one, and came about when I decided to remove a Zendesk widget which was more or less a feedback message system for our web application. I decided it made more sense to remove the Zendesk widget and just build a global livewire component, mainly due to the fact that I could not add css to the widget and move it for when we have a mobile sticky footer in the app. Could I have added some javascript and add some style that way, probably but to me that seemed rather hacky and if a backend developer thinks a frontend idea is hacky, well then I think we can all agree it's extremely hacky.
So in comes livewire!!!!!! What is livewire you ask? Well if you don't know I 100% highly recommend looking Laravel Livewire. If you don't feel like reading a little or don't know what it is I'll give you the most high level overview it. So livewire allows us laravel (backend devs) to write reactive front end components using 0 javascript?!?!?!?!?!? How you ask, well to me its magic. Could I learn how it all really works? Sure it's possible but at my age I've come to a realization that some people are just wizards, magicians, or on some alien type level. Caleb Porzio is one of those people. How one earth he came up with this honestly I couldn't even begin to imagine. On the topics of aliens, Adam Wathan (creator of Tailwind CSS) is another one of those non humans.
So if you've never given livewire and you're a laravel developer do yourself a favor and play with it, you won't regret it. It's still so new and I'm not 100% sure sold on if it will be relevant down the line, but let's be honest, we don't know what's gonna happen tomorrow so these are future Brad problems. I digress and have gone off topic for long enough now. So back the main point of this blog and the funny behavior I encountered while using livewire.
First thing first there is already one instance where the Zendesk widget is not used, and it's whenever a customer clicks message us on the listings page. This brings them to a form which has some basic inputs (name / email / phone etc....) that are stored on the Submission model along with a meta field that can be any named array pairs. Whenever a Submission is created in this particular component, an email is sent to our Admins along with a slack notification. So I figured why reinvent the wheel and let's just make a new livewire component that acts the same way except the meta fields are different.
So this app makes use of laravels blade components, which if you haven't used are pretty cool. They are basically slots that benefits layouts / sections (reusable content). So in our app everything is sent through the app-layout component. So this is where I will add my livewire support message component. I do however need to make a quick conditional to not display it on a few pages since we are using another submission method there. Taking advantage of laravels global methods I came up with this:
@if( !request()->routeIs('charters.show') && !request()->routeIs('pages.message-us') )
<livewire:support-message />
@endif
and bam perfect. And my livewire support-message.blade.php
file is a basic form with a conditional to show the form. Ah what the heck I'll put it here. Here you go:
<div>
@if(!$show_form)
<div wire:click="displayForm()" class="fixed bottom-14 right-2 md:bottom-0 py-1 px-4 rounded-tl-lg rounded-tr-lg bg-yellow-500 hover:bg-yellow-600 flex items-center cursor-pointer z-50">
<i class="fal fa-envelope text-white text-xl font-bold mr-3"></i><span class="text-base font-bold text-white">Leave a Message</span>
</div>
@else
<div class="fixed bottom-14 right-2 md:bottom-0 shadow-lg z-50 max-h-96 overflow-scroll">
<div class="bg-yellow-500 flex items-center justify-between py-2 pl-4 pr-2 rounded-tl-lg rounded-tr-lg">
<span class="text-white text-base">FishAnywhere Support</span>
<span class="text-white text-xl mr-2 font-bold cursor-pointer" wire:click="displayForm()"><i class="fal fa-times"></i></span>
</div>
<div class="flex flex-col bg-gray-200 px-4">
<div class="my-1">@include('partials._text-field-input', ['model' => 'name', 'label' => 'Name *'])</div>
<div class="my-1">@include('partials._text-field-input', ['model' => 'email', 'label' => 'Email *'])</div>
<div class="my-1">@include('partials._text-field-input', ['model' => 'phone_number', 'label' => 'Phone Number *'])</div>
<div class="my-1">@include('partials._text-area-input', ['model' => 'comments', 'label' => 'Comments *'])</div>
<div class="my-1 pb-4"><button wire:click="save()" class="text-white bg-yellow-500 hover:bg-yellow-600 rounded-lg py-1 px-4">Submit</button></div>
</div>
</div>
@endif
</div>
So in my livewire SupportMessage component I have just a few basic methods. The render (which by default has to return a view), a displayForm() which just sets the property opposite to what it currently is, and the save method where I validate inputs, create a submissions, and fire off some events. Yes I know what you're thinking that seems to violate the single responsibility principle but since this is a one off component I'm ok with that. And now we can finally get to the actual point of this post. So my original plan was to grab the current route in the save method and save it as meta. And here is where the fun-ish stuff happened.
Laravel has a few global methods that I take advantage of quite a bit. route()
, request()
,url()
, and logger()
are a few that I use the most. I use these in blade directives / controllers, you name it. Normally to get the current route I would do something like: request()->route()->getName()
and if all of your routes are using the named route modifier it will grab it. If you want the current url you can use url()->current()
. So in our route files every single route are named, that being said the Admins whom are going to see these submissions, well even as intuitive as are named routes are, I am not 100% sure they would be able to decipher them all. So initially I threw:
$meta = [ 'url' => url()->current() ];
inside of my save method and after I create my submission I set $submission->meta = $meta
. Obviously none of this will happen unless the inputs are validated. After my first submission the Url displayed in the slack / email was http://127.0.0.1:8000/livewire/message/support-message
, and I submitted the message on the home page. I'm not really sure why this is happening, I can only assume that it has something to do with livewires routing under the hood, and as I explained before I'm not gonna try to understand the genius of Caleb and I'm just going to have to figure a work around.
So I know with livewire I can pass data to the mount method (acts like a constructor) and I came up with:
<livewire:support-message url="{{ url()->current() }}"/>
Then in my mount method of the component:
public function mount(string $url)
{
$this->url = $url;
}
**UPDATE**
So while writing this blog I played around with it and found even a cleaner way of doing this. So I guess in the mount method you can in fact use the url()
method. Why? Again, no idea and don't need to dig into the heart of livewire. So this simplifies it quite a bit and now I get:
<livewire:support-message />
And then back to the mount of the component:
public function mount()
{
$this->url = url()->current();
}
So basically what I've learned is that now you can use all of laravel global methods in livewire components, but when you do they need to be used in the mount (constuctor) or they might have side effects that you do not expect. Sorry this particular post got a little side tracked with random background, non useful items, but hey I'm new to this and I'm hoping to improve week by week. Until next time.
23