Refactoring Laravel with conditional classes - @class

Refactoring Laravel with conditional classes - @class

Realm Coding

Have you ever taken a second look at your code and felt like you could do it in a better way, maybe cleaner... Or, have you ever looked at an old project after learning new techniques and being like... well... "I could really apply my new knowledge to this and make it 🎵 better faster stronger 🎵". 
If the answer is yes then you have the refactoring bug! 

What's this all about

In this series of posts (Refactoring Laravel), I will be exploring the sudorealm codebase and refactoring bits of code here and there with new techniques and Laravel findings.

The @class Blade Directive

From the Laravel Docs: The @class directive conditionally compiles a CSS class string. The directive accepts an array of classes where the array key contains the class or classes you wish to add, while the value is a boolean expression. If the array element has a numeric key, it will always be included in the rendered class list:

@php
    $isActive = false;
    $hasError = true;
@endphp
 
<span @class([
    'p-4',
    'font-bold' => $isActive,
    'text-gray-500' => ! $isActive,
    'bg-red' => $hasError,
])></span>
 
<span class="p-4 text-gray-500 bg-red"></span>

So what you are telling me is, that p-4 will always be rendered and then the we get the other classes based on the value of the boolean.

"Well... ok fair enough, that's cool and all but where could I implement this?" Was my initial thought, therefore I started scribbling down the Sudorealm code until I found the following snippet. 

@if ($hasCrowned)

{{-- CROWNED --}}
<button wire:click.prevent="crown"
    class="flex items-center justify-center w-10 h-10 text-yellow-400 transition duration-150 ease-in bg-indigo-500 border border-indigo-500 rounded-full shadow cursor-pointer hover:bg-indigo-50 hover:shadow-none hover:text-indigo-200">
    <svg class="w-5 h-auto fill-current" viewBox="0 0 24 24">
        <path fill-rule="evenodd" clip-rule="evenodd"
            d="M2.5 6.09143L7.21997 10.8114L12.0005 6.03088L16.7811 10.8114L21.5 6.09245V14.9691C21.5 16.626 20.1569 17.9691 18.5 17.9691H5.5C3.84314 17.9691 2.5 16.626 2.5 14.9691V6.09143ZM19.5 10.9087V14.9691C19.5 15.5214 19.0523 15.9691 18.5 15.9691H5.5C4.94771 15.9691 4.5 15.5214 4.5 14.9691V10.9077L7.21997 13.6277L12.0005 8.84717L16.7811 13.6277L19.5 10.9087Z" />
    </svg>
</button>

@else

{{-- NOT CROWNED --}}
<button wire:click.prevent="crown"
    class="flex items-center justify-center w-10 h-10 transition duration-150 ease-in border border-indigo-500 rounded-full shadow cursor-pointer bg-indigo-50 hover:bg-indigo-500 hover:shadow-none hover:text-yellow-400">
    <svg class="w-5 h-auto fill-current" viewBox="0 0 24 24">
        <path fill-rule="evenodd" clip-rule="evenodd"
            d="M2.5 6.09143L7.21997 10.8114L12.0005 6.03088L16.7811 10.8114L21.5 6.09245V14.9691C21.5 16.626 20.1569 17.9691 18.5 17.9691H5.5C3.84314 17.9691 2.5 16.626 2.5 14.9691V6.09143ZM19.5 10.9087V14.9691C19.5 15.5214 19.0523 15.9691 18.5 15.9691H5.5C4.94771 15.9691 4.5 15.5214 4.5 14.9691V10.9077L7.21997 13.6277L12.0005 8.84717L16.7811 13.6277L19.5 10.9087Z" />
    </svg>
</button>

@endif

👹 It's a monstrosity of a code, and it does a very simple thing actually. The logic follows:  "Has the user crowned the post? If yes show the crown button with the appropriate CSS to let them know". That's it... If you've roamed around the realm a bit you should have come across it, it looks something like this, 

crowning animation on sudorealm

We should refactor that monster to something cleaner! We have the refactoring bug after all! 

Refactoring monster code!

Let's think about it a little... What differs between the two classes?

class="flex items-center justify-center w-10 h-10 text-yellow-400 transition duration-150 ease-in bg-indigo-500 border border-indigo-500 rounded-full shadow cursor-pointer hover:bg-indigo-50 hover:shadow-none hover:text-indigo-200"

class="flex items-center justify-center w-10 h-10 transition duration-150 ease-in border border-indigo-500 rounded-full shadow cursor-pointer bg-indigo-50 hover:bg-indigo-500 hover:shadow-none hover:text-yellow-400"

{{-- CROWNED
   text-yellow-400 
   bg-indigo-500
   border-indigo-500
   hover:bg-indigo-50 
   hover:text-indigo-200
--}}

{{-- NOT CROWNED
   border-indigo-500
   bg-indigo-50 
   hover:bg-indigo-500  
   hover:text-yellow-400
--}}

These are the classes that differ from the two buttons that we want to refactor to one. 

Next step is to apply the @class conditional directive just like the docs showed as how.

@class([
    'flex items-center justify-center w-10 h-10 transition 
     duration-150 ease-in  border rounded-full shadow cursor-pointer hover:shadow-none',

    'text-yellow-400 bg-indigo-500 border-indigo-500 
     hover:bg-indigo-50 hover:text-indigo-200' 
            => $hasCrowned,

     'border-indigo-500 bg-indigo-50 hover:bg-indigo-500 hover:text-yellow-400 ' 
            => ! $hasCrowned
    ])

The final result is, 

<button wire:click.prevent="crown"
   @class([
    'flex items-center justify-center w-10 h-10 transition 
     duration-150 ease-in  border rounded-full shadow cursor-pointer hover:shadow-none',

    'text-yellow-400 bg-indigo-500 border-indigo-500 
     hover:bg-indigo-50 hover:text-indigo-200' 
            => $hasCrowned,
     'border-indigo-500 bg-indigo-50 hover:bg-indigo-500 hover:text-yellow-400 ' 
            => ! $hasCrowned
    ])>
    <svg class="w-5 h-auto fill-current" viewBox="0 0 24 24">
        <path fill-rule="evenodd" clip-rule="evenodd"
            d="M2.5 6.09143L7.21997 10.8114L12.0005 6.03088L16.7811 10.8114L21.5 6.09245V14.9691C21.5 16.626 20.1569 17.9691 18.5 17.9691H5.5C3.84314 17.9691 2.5 16.626 2.5 14.9691V6.09143ZM19.5 10.9087V14.9691C19.5 15.5214 19.0523 15.9691 18.5 15.9691H5.5C4.94771 15.9691 4.5 15.5214 4.5 14.9691V10.9077L7.21997 13.6277L12.0005 8.84717L16.7811 13.6277L19.5 10.9087Z" />
    </svg>
</button>

That's uncomperably cleaner, don't you agree? Well if you agree, I can't argue with you... but let's go a step further shall we? We are writing code with a razor sharp blade after all! 

Let's slice the ugly SVG code and put it into its own component inside a folder named SVG and let's call the component crown 👑. 

<button wire:click.prevent="crown"
   @class([
    'flex items-center justify-center w-10 h-10 transition 
     duration-150 ease-in  border rounded-full shadow cursor-pointer hover:shadow-none',

    'text-yellow-400 bg-indigo-500 border-indigo-500 
     hover:bg-indigo-50 hover:text-indigo-200' 
            => $hasCrowned,
     'border-indigo-500 bg-indigo-50 hover:bg-indigo-500 hover:text-yellow-400 ' 
            => ! $hasCrowned
    ])>
    <x-svg.crown/>
</button>

Now we are talking! Now we have beautifully refactored the code and have successfully implemented the very helpful @class blade directive. Our Job is finished! End of article. Full Stop. Close the curtains, and thank you all for coming to the show 🎻👏!!!  
Except if... We could maybe just do some... 

More Refactoring!

Yes, yes... some more... This button could really become a reusable component, if we play our cards right. 
There is a folder in the Sudorealm codebase for button components, so if we created a new component file named crown under that folder we would be able to call it like,

<x-button.crown />

Just like that, but I wouldn't say that wire:click.prevent="crown"is really reusable, so we will just use the Blade Component Attributes. Our final component code will look like this, 

<button
    {{ $attributes }}
    @class([
        'flex items-center justify-center w-10 h-10 transition duration-150 ease-in  border rounded-full shadow cursor-pointer hover:shadow-none',
        'text-yellow-400 bg-indigo-500 border-indigo-500 hover:bg-indigo-50 hover:text-indigo-200' 
            => $hasCrowned,
        'border-indigo-500 bg-indigo-50 hover:bg-indigo-500 hover:text-yellow-400 ' 
            => ! $hasCrowned
    ])
    >
    <x-svg.crown />
</button>

and now we can call our new fully refactored button like this, 

<x-button.crown wire:click.prevent="crown" 
                :hasCrowned="$hasCrowned" />

a true sight for sore eyes.

Conclusion

This is the first Laravel post of many and many to come, from simple quality changes to entire reusable livewire components. I hope that I will manage to cover a great variety of tips and tricks of the TALL Framework.

Stay tuned 📻 Share the post if you liked it, and If you loved it... Give it a Crown 👑! 😉 

Learn more about the Sudorealm Project, we are constantly trying to make it better and we are so thankful that you have clumsily stumbled upon us! Hope you like our style. 

I am @d3ad R1nger, Creator of Sudorealm, thank you for reading. 

🐥 @DevThanos

d3ad R1nger buymeacoffee

 

 

 

 

Affiliate Links

Check out what d3ad R1nger suggests for Refactoring Laravel with conditional classes - @class!

Related Posts

Thursty for more? Check out Sudorealm-related posts!