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
Related
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.
I have 4 livewire components, rendered in dashboard as:
#livewire('profits.stats')
#livewire('costs.stats')
#livewire('leads.stats')
#livewire('sales.stats')
However, when page loads they run one after another instead of asyncronously. I put a sleep(3) to test, and each starts after the previous ones 3 second delay. How to get them to all start same time?
toggleShowData below has the sleep method, and sets showData=true. I saw this in a tutorial for now to have a loading for individual components
I don't think its relevant, but here is my template:
<div wire:init="toggleShowData">
<div wire:loading.delay class="align-items-center">
<div class="flex items-center justify-center ">
<div class="w-40 h-40 border-t-4 border-b-4 border-brand-500 rounded-full animate-spin"></div>
</div>
</div>
<div class="flex-auto p-4 bg-brand-300 rounded" wire:loading.remove>
<div class="flex flex-wrap">
<div class="relative w-full pr-4 max-w-full flex-grow flex-1">
<h5 class="text-gray-100 uppercase font-bold text-xs"> Costs</h5>
<span class="font-semibold text-xl text-gray-300">$34,100</span>
</div>
<div class="relative w-auto pl-4 flex-initial">
<div
class="text-white p-3 text-center inline-flex items-center justify-center w-12 h-12 shadow-lg rounded-full bg-red-500">
<i class="fas fa-chart-bar"></i>
</div>
</div>
</div>
<p class="text-sm text-gray-100 mt-4">
<span class="text-emerald-500 mr-2"><i class="fas fa-arrow-up"></i> 2,99% </span>
<span class="whitespace-nowrap"> Since last month </span></p>
</div>
</div>
The first render of the livewire page happens inside the PHP process in the backend.
Blade sees that you are requesting a livewire component and replaces this with the result of your parse() method from your component.
This indeed happens in sync, once the page is rendered the components can render itself async
<div class="col-6 col-md-4 col-lg-3 mb-4 mb-sm-5">
<div class="col-12 h-100 c-bg-offwhite p-0">
<div class="row">
<div class="col-12 text-center">
<img class="w-100" src="{{ url('/storage/photo_images/'.$image) }}" alt="">
</div>
<div class="col-12 text-left">
<p class="p color-black d-block px-4 pt-4 pb-3 mt-0 mb-0">{{$photo->title}}</h3>
</div>
#guest
<div class="col-12">
<div class="px-4 pb-4">
<a href="/contact">
<button class="w-100 btn-primary">Get a quote</button>
</a>
</div>
</div>
#endguest
</div>
</div>
#auth
<div class="col-12 p-0 mt-3">
<a href="/photos/{{$photo->slug}}/edit">
<button class="w-100 btn-primary">Edit</button>
</a>
</div>
#endauth
</div>
The code is a photo, with a title and a button for customers to click to contact the company. When an end-user is logged in it turns into an edit button, but sits underneath so not to confuse it with the customer facing button. It should be a case of one button on, one button off.
auth
guest
I'm so sorry for not really understanding your question.
But based on you question and the screenshot you provides it's already do what it shoud do.
#guest will be showing a button with 'Get A Quote' written on it and links to /contact
and #auth will be showing edit button when a user is logged in.
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>
I am following along with the Laracast lab, the Tweety feed and all was going good until i hit a wall.
I have a PATCH to update the user profile and it works and updates the record, but the validation doesn't. The form submits if fully complete and the record is fine but if the form validation fails server side in my controller, I get as below:
Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException
The GET method is not supported for this route. Supported methods: POST.
I have looked at the FAQ section here and scoured the net already asked and tried several steps to sort this. I have tried:
Using $user->username in my edit file template and controller.
Changing the route from PATCH TO PUT (and also POST, simply using store)
Checking the User model and ensuring I am using 'getRouteKeyName' working correctly, it is.
Checking if there is a conflict in the route, there is none and the order/VERB is fine.
Can anyone please help? Below is my code.
Routes as below:
// Snippet for seeing all the database queries.
//DB::listen(function($query){var_dump($query->sql, $query->bindings);});
use Illuminate\Support\Facades\Route;
// Home view, no sign-in required.
Route::get('/', function () {
return view('welcome');
});
// These routes require and use auth middleware.
Route::middleware('auth')->group(function(){
// TWeets main page.
Route::get('/tweets/', 'TweetsController#index')->name('home');
// Store a new tweet
Route::post('/tweets/', 'TweetsController#store');
// Profile area, view and update
// Use the 'name' attribute in route/model binding instead of the primary key = user:name
Route::get('/profile/{user:username}/', 'ProfileController#show')->name('profile');
// Show form to edit the profile
Route::post('/profile/{user:username}/edit','ProfileController#edit')->name('edit_profile');
//->middleware('can:edit,user'); // Don't forget the wildcard.
// Finally, update the users profile information.
Route::patch('/profile/{user:username}','ProfileController#update')->name('update_profile');
// Handles the follow/unfollow on a toggle method.
Route::post('/profile/{user:username}/follow/','FollowsController#store');
// Logs you out & directs to the homepage.
Route::get('/logout', '\App\Http\Controllers\Auth\LoginController#logout');
});
Auth::routes();
Controller as below:
/**
* Update the users profile after validation
*
* #param \Illuminate\Http\Request $request
* #param int $id
* #return \Illuminate\Http\Response
*/
public function update(User $user)
{
$validated = request()->validate([
'name'=>'required',
'username'=>'required',
'password'=>'required',
'password_confirmation'=>'required',
]);
$user->update($validated);
// Redirect to the tweets page which is called 'home'.
return redirect(route('profile', $user->username));
}
Template:
<x-app>
<form class="w-full" method="POST" action="{{route('update_profile',$user)}}">
#method('PATCH')
#csrf
<h2 class="text-xl mb-2 py-4 font-bold">Edit profile for {{$user->username}}</h2>
<p class="text-sm mb-5 mt-0">Below you an update your profile details.</p>
<div class="flex flex-wrap -mx-3 mb-6">
<div class="w-full px-3 mb-6 md:mb-0">
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-name">Name</label>
<input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" id="name" type="text" placeholder="Mike Smith.." name="name" value="{{$user->name}}"/>
</div>
#error('name')
<p>{{$message}}</p>
#enderror
<div class="w-full px-3 mb-6 md:mb-0">
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-username">Username</label>
<input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" id="grid-username" type="text" placeholder="MikeSmith1..." name="username" value="{{$user->username}}"/>
</div>
#error('username')
<p>{{$message}}</p>
#enderror
<div class="w-full px-3 mb-6 md:mb-0">
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-username">Email</label>
<input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-red-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" id="grid-username" type="email" placeholder="MikeSmith#yahoo.com..." name="email" value="{{$user->email}}"/>
</div>
#error('email')
<p>{{$message}}</p>
#enderror
<div class="w-full px-3">
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-password">Password</label>
<input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white focus:border-gray-500" id="grid-password" type="password" name="password"/>
<p class="mb-3 text-red-600 text-xs italic">Make it as long and as crazy as you'd like</p>
</div>
<div class="w-full px-3"> {{-- This HAS to be called "password_confirmation" --}}
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-password">Password Confirmation</label>
<input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white focus:border-gray-500" id="grid-password_confirmation" type="password" name="password_confirmation"/>
</div>
<button type="submit" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 ml-2 mt-4 px-4 rounded">
Update details
</button>
</div>
</form>
</x-app>
Model:User
/**
* Laravel <=6 - use this method, after v6, you can do this on the route itself.
* Using this so we can use a user name in the route, instead of the ID Laravel normally uses for route/model binding. Now you can use /profile/mikethornley
* This will still enable route/model binding to work and still find the user
* #return [type] [description]
*/
public function getRouteKeyName()
{
return 'username';
}
This is the problem:
// Use the 'name' attribute in route/model binding instead of the primary key = user:name
Route::get('/profile/{user:username}/', 'ProfileController#show')->name('profile');
// Finally, update the users profile information.
Route::patch('/profile/{user:username}','ProfileController#update')->name('update_profile');
Your routes are clashing... that post ends up in the show() method and that's why you get the error.
You need to make some change in that post route if you want to have parameters. For example
Route::patch('/profile-update/{user:username}','ProfileController#update')->name('update_profile');