How can I set a session variable conditionally in a blade template? - laravel

I am developing an app in Laravel 9 and am trying to add Cancel functionality to my Create template. I want the form to have two buttons: Submit and Cancel. I asked how to add Cancel functionality to my form last night in this question and got a couple of answers that work. However, it struck me that there might be another way to do it, namely having both buttons invoke the create() method of the controller and then for the create() method to determine which button was pushed, then do different logic based on which button was pressed. I'm imagining a session variable being set to "create" if the Create button is clicked and for the session variable to be set to "cancel" if the Cancel button is clicked.
If the Cancel button was pressed, the create() method would bypass validations and then redirect back to the index page with the message "Create() cancelled"; otherwise, if the Create button was pressed, the create() method would do all the validations, add the new record to the database, and then redirect to the index page with the message "Create was successful".
I have no concerns about getting the create() method to work but I'm having a challenge setting a session variable in my create template based on which button was pressed. I've tried several variations but all of them return "Cancel" REGARDLESS of which button was pressed! I don't understand this behaviour at all: clearly, Laravel doesn't work in the way that I imagine.
I researched how to set a session variable in a blade file here at StackOverflow but couldn't see any examples where the value of the variable was set conditionally so I clearly need some help with that part.
Here's my latest attempt at the template:
<x-layout>
<x-card xclass="p-10 max-w-lg mx-auto mt-24">
<h1 class="text-3xl text-center text-white font-bold bg-indigo-700">Create a New Non-driving Reason</h1>
<h1 class="pb-5"></h1><!-- spacer beneath title -->
<form method="POST" action="/nonDrivingReasons/store" enctype="multipart/form-data">
#csrf
<div class="mb-6">
<label for="reason_for_not_driving" class="inline-block text-lg mb-2">Reason for not driving</label>
<input type="text" class="border border-gray-200 rounded p-2 w-full" name="reason_for_not_driving" value="{{old('reason_for_not_driving')}}"/>
#error('reason_for_not_driving')
<p class="text-red-500 text-xs mt-1">{{$message}}</p>
#enderror
</div>
<div class="mb-6">
{{-- Submit the completed form --}}
<button class="text-xl text-white bg-green-500 rounded-md py-2 px-4 hover:bg-black" onClick="#php session()->put('button', 'submit') #endphp">Submit</button>
{{-- Cancel submission of the form --}}
<button class="text-xl text-white bg-black rounded-md py-2 px-4 hover:bg-red-300" onClick="#php session()->put('button', 'cancel') #endphp">Cancel</button>
</div>
</form>
</x-card>
</x-layout>

You cannot set session like this onClick="#php session()->put('button', 'submit') #endphp" because, php is server side scripting language and javascript is clientside, SO I suggest you to use little bit of js code instead of setting session,
Try using button as below:
<button class="text-xl text-white bg-green-500 rounded-md py-2 px-4 hover:bg-black" type="button">Submit</button>
{{-- Cancel submission of the form --}}
<button class="text-xl text-white bg-black rounded-md py-2 px-4 hover:bg-red-300" type="button" >Cancel</button>
</div>
And this small piece of javascript which help you to send which button you have clicked on the request:
<script>
document.querySelector("button").addEventListener("click", (e) => {
e.preventDefault()
document.getElementById("button").value = e.target.innerText;
document.querySelector('form').submit()
});
so in Controller if you do dd($request->button); you can get the value text of button that you have clicked.

Related

Alpine Modal open on page load, but nonce closed don't open on new page loads

I'm having some trouble with the $persist plugin. My modal opens on page load as expected. However when I close the modal and go to another page, I don't want it to open again.
https://codepen.io/createsean/pen/MWQOvpx
codepen above shows working modal but this loads on all page loads - javascript clicks hidden button on page load.
What I want to do is have the modal show on the first page load and then subsequent page loads not show up. I think that I need to use the $persist plugin and the $store magic
However I am unable to figure out how to get this working please advise.
HTML
<div x-data="{ noticeModal: $persist(false).as('notice-modal').using(sessionStorage) }" class="flex justify-center">
<!-- Trigger -->
<span x-on:click="noticeModal = true" id="open-notice-modal" class="hidden">
<button type="button" class="bg-white px-5 py-2.5 rounded-md shadow">
Open dialog
</button>
</span>
<p class="my-12 text-2xl max-w-2xl mx-auto">A hidden button gets clicked on page load + 2 seconds and opens the modal. But it does it on every page load, when it should only be on first page load.</p>
<!-- Modal -->
<div
x-show="noticeModal"
style="display: none"
x-on:keydown.escape.prevent.stop="noticeModal = false"
role="dialog"
aria-modal="true"
x-id="['modal-title']"
:aria-labelledby="$id('modal-title')"
class="fixed inset-0 z-50 max-w-lg mx-auto overflow-y-auto"
x-transition:enter.duration.300ms
x-transition:leave.duration.200ms>
<!-- Overlay -->
<div x-show="open" x-transition.opacity class="fixed inset-0 bg-black bg-opacity-50"></div>
<!-- Panel -->
<div
x-show="open" x-transition
x-on:click="noticeModal = false"
class="relative flex items-center justify-center min-h-screen p-4">
<div
x-on:click.stop
x-trap.noscroll.inert="noticeModal"
class="relative w-full max-w-2xl p-12 overflow-y-auto bg-white shadow-lg">
<!-- Title -->
<h2 class="mt-4 mb-4 text-3xl font-bold text-center uppercase text-pinkBrand" :id="$id('modal-title')">My Title</h2>
<!-- Content -->
<div class="prose">
some copy here
</div><!-- /.prose -->
<button
type="button"
x-on:click="noticeModal = false"
class="absolute top-4 right-4">
X
</button>
</div>
</div>
</div>
</div>
javascript
// wait for dom to load then click the button that triggers the modal
document.addEventListener("DOMContentLoaded", function(event) {
let pagebutton= document.getElementById("open-notice-modal");
// load modal after 2 seconds
setTimeout(() => {
pagebutton.click();
}, "2000")
});
You should create an independent state variable in the sessionStorage that tracks only whether the modal window was opened or not. Let's call it noticeModalOpened. In the x-init attribute (so we don't have to write the extra JS), we first check noticeModalOpened state variable from the sessionStorage and enqueue an opening event only on the first page load. After the opening event we flip this state variable as well, so next time we wont open the modal again.
<div x-data="{ noticeModal: false, noticeModalOpened: $persist(false).as('notice-modal').using(sessionStorage) }"
x-init="if (!noticeModalOpened) {setTimeout(() => {noticeModal = true, noticeModalOpened = true}, 2000)}">
<!-- Trigger -->
<span #click="noticeModal = true, noticeModalOpened = true" id="open-notice-modal">
<button type="button" class="bg-white px-5 py-2.5 rounded-md shadow">
Open dialog
</button>
</span>
<!-- Modal -->
</div>

Laravel, Inertia.js and vue, check if user is logged In

I'm using laravel and inertia.js to implement my project. In my navbar I want to display div element with some user details if the user is logged in. And if the user is not logged in the div should not appear. I have tried this but its not showing the details neither when I am logged in nor
when I'm not. What should I do?
<div class="ml-3 relative" v-if="$page.props.auth.user">
<div>
<button #click="dropDown=true"
class="bg-gray-800 flex text-sm rounded-full focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-white"
id="user-menu" aria-haspopup="true">
<span class="sr-only">Open user menu</span>
<img class="h-8 w-8 rounded-full object-cover" :src="$page.props.user.profile_photo_url"
:alt="$page.props.user.name"/>
</button>
</div>
<div v-if="dropDown"
class="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5"
role="menu" aria-orientation="vertical" aria-labelledby="user-menu">
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" role="menuitem">
<form method="POST" #submit.prevent="logout">
<jet-responsive-nav-link as="button">
Logout
</jet-responsive-nav-link>
</form>
</a>
</div>
</div>
On AppServiceProvider
public function boot()
{
//check if user is logged in
Inertia::share('auth.user', function() {
return ['loggedIn' => Auth::check()];
});
}
With Inertia you can create a HandlerInertiaRequest Middlware.
The HandleInertiaRequests middleware provides a "share" method where you can define the data that is automatically shared with each Inertia response. In Your case thats the user which is logged in.
You can find more infos in the docs Shared Data.
They have an example in there demo app repository.
In your view you can check if the user is logged in by checking the shared data user prop.
v-if="$page.props.auth.user"
Laravel 8.4.0 with InertiaJS returns $page.props.user by default if user logged in (see this answer). So you can simply do,
<element v-if="$page.props.user">This will only show when a user logged in.</element>
I guess you can follow the same approach built here in this middleware ,
https://github.com/inertiajs/pingcrm/blob/master/app/Http/Middleware/HandleInertiaRequests.php
and then you separated the logic from the providers

Laravel and Livewire change button style on click

I have two buttons in tailwindcss in my
<div class="flex flex-row">
<button class="-ml-px relative inline-flex items-center space-x-2 px-4 py-4 border border-blue-300 text-sm font-medium rounded-l-md text-white bg-blue-500 hover:bg-blue-600 focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-blue-500">
<span>Add</span>
</button>
<button class="-ml-px relative inline-flex items-center space-x-2 px-4 py-4 border border-gray-300 text-sm font-medium text-gray-900 bg-gray-50 hover:bg-gray-100 focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-blue-500">
<span>Set</span>
</button>
</div>
one button is blue and other is grey.
I want to switch colour when I click on Set Button and also same when I click back to Add button again.
As I mentioned, Livewire is for interaction with backend code. If you want to style frontend elements following frontend interaction, use a JS Framework like AlpineJS or plain CSS.
If you really just want to change focus color, you can go with a variation of #Digvijay's answer:
<div class="flex space-x-4">
<button class="flex px-4 py-2 bg-gray-100 text-gray-900 cursor-pointer hover:bg-blue-200 focus:text-blue-700 focus:bg-blue-200 focus:outline-none focus:ring-blue-600" tabindex="1">Add</button>
<button class="flex px-4 py-2 bg-gray-100 text-gray-900 cursor-pointer hover:bg-blue-200 focus:text-blue-700 focus:bg-blue-200 focus:outline-none focus:ring-gray-600" tabindex="2">Set</button>
</div>
(see https://play.tailwindcss.com/mwspfpsTuU)
If you want the colors to stick even after the focus is lost, you may use something like this with AlpineJS:
<div x-data="{ highlightedButton: '' }" class="flex space-x-4">
<button #click="highlightedButton='add'" class="flex px-4 py-2 bg-gray-100 text-gray-900 hover:bg-blue-200" :class="{'bg-blue-400': highlightedButton === 'add'}" tabindex="1">Add</button>
<button #click="highlightedButton='set'" class="flex px-4 py-2 bg-gray-100 text-gray-900 hover:bg-blue-200" :class="{'bg-blue-400': highlightedButton === 'set'}" tabindex="2">Set</button>
</div>
And finally if you keep track of the Add / Set state in the Livewire component anyways (which is hard to tell because there is no livewire code in your markup at all), then do it like #AliAli described in his answer. :-)
Edit:
If anyone came to this answer, this is a solution when storing the button state is needed, in other cases, you should follow #Lupinity Labs answer.
You can use Livewire event listeners to change the frontend.
First, declare variables inside the Livewire PHP model like:
public $isSetClicked = false;
public $isAddClicked = false;
Then decalre functions to handle the onClick event:
public function SetClicked()
{
//this will give you toggling behavior
$this->isSetClicked == false ? $this->isSetClicked = true : $this->isSetClicked = false;
}
and declare the AddClicked the same way.
Then inside your Livewire template add the events to the buttons like:
<button wire:click="SetClicked()" class="{{$isSetClicked ? "color-class" : "other-color-class"}} the rest of your css classes">
To toggle HTML attributes with livewire, for example class, id, href and so on you have to work with AlpineJS inside livewire (docs). For example you want to change some element CSS class attribute by changing a some internal state (model) inside your livewire component. For example by clicking a some link.
/app/Http/Livewire/YourLivewireComponent.php
<?php
namespace App\Http\Livewire;
use Livewire\Component;
class YourLivewireComponent extends Component
{
public $classChanged = false;
public function render()
{
return view('livewire.your-liveweire-component');
}
public function myClickFunction()
{
$this->classChanged = true;
}
}
/resources/views/livewire/your-livewire-component.blade.php
Click me
<div x-data="{ alpine_class_changed: #entangle('classChanged') }" class="some classes" x-bind:class="alpine_class_changed ? 'my_add_class' : ''">some text</div>
/resources/views/somelaraveltemplate.blade.php
<p>some HTML code</p>
#livewire('your-liveweire-component')
You will need to use tabindex. Checkout focus change on button working example.
<div class="flex space-x-4">
<div class="flex px-4 py-2 bg-blue-600 text-blue-100 cursor-pointer hover:bg-blue-700 focus:text-blue-700 focus:bg-blue-200 focus:outline-none focus:ring-blue-600" tabindex="1">Add</div>
<div class="flex px-4 py-2 bg-gray-100 text-gray-600 cursor-pointer hover:bg-gray-200 focus:text-gray-100 focus:bg-gray-600 focus:outline-none focus:ring-gray-600" tabindex="1">Set</div>
</div>

Laravel live wire accordion collapsing on input change

I am using accordion something like this
https://www.tailwindtoolbox.com/components/accordion and I have a input field inside my accordion
<div class="tab-content overflow-y-scroll border-l-2 bg-gray-100 border-indigo-500 leading-normal">
<div class="border border-black mt-3 p-3 grid grid-cols-4">
<div class="col-span-1">
<label for="about" class="block text-sm leading-5 font-medium text-gray-700">
Title
</label>
<div class="rounded-md shadow-sm">
<textarea wire:model="additional_docs.title" name="additional_docs" id="edit_additional_docs" name="" rows="3" class="form-textarea mt-1 block w-full transition duration-150 ease-in-out sm:text-sm sm:leading-5" placeholder="description"></textarea>
</div>
<p class="mt-2 text-sm text-gray-500">
Document Title
</p>
</div>
</div>
The accordion is working fine but as soon as I start typing the accordion closes and I need to open accordion again and start typing, but if I remove wire:model ,it works fine .I am new to live wire and if I use wire:ignore it doesn't help too.
Thanks for any help :)
Add wire:ignore.self on the element which will be hidden/shown.

Laravel Form Button Cancel

Using Laravel, I have a form and I want to include a "cancel" button that just redirects back to a different route. Since the button is within the form element, when clicked it attempts to submit the form. In the Laravel docs I don't see a good way to do this. Any suggestions or would using Javascript be best?
Thanks,
<div class="form-group pull-right">
<button class="btn btn-default btn-close">Cancel</button>
<button type="submit" class="btn btn-global">Register</button>
</div>
Though it's a very late answer, But it might help others.
You can just redirect back where you have come from. Simply use the following code.
Cancel
It will send you back where you have came.
<a> elements in bootstrap can have the btn class and they will look just like a button, so you can take the button out and just have cancel be a link:
<div class="form-group pull-right">
<a class="btn btn-default btn-close" href="{{ route('home') }}">Cancel</a>
<button type="submit" class="btn btn-global">Register</button>
</div>
it does not have to do with laravel. You can simple use formaction attribute
<input type=submit formaction="anotherpage">
<button class="btn btn-default btn-close" formaction="{{ route('home') }}">Cancel</button>

Resources