I'm new to Livewire with some laravel experience and a good amount of php experience. My issue I'm trying to get a livewire search component to work. I'm using laravel 8 and livewire is installed. My nav menu is using livewire. SO I wish to insert a search into my layout as follows:
<body class="font-sans antialiased">
<x-jet-banner />
<div class="min-h-screen bg-gray-100">
#livewire('navigation-menu')
#livewire('search-accounts')
... rest of my layout ...
</div>
</body>
I have a Model Account which has the Accounts used by my app. Account uses Eloquent and is working fine.
I have created a livewire component app/Http/Livewire/SearchAccounts.php with the following content:
<?php
namespace App\Http\Livewire;
use App\Models\Account;
use Livewire\Component;
class SearchAccounts extends Component
{
public $search = '';
public function render()
{
return view('livewire.search-accounts', [
'accounts' => Account::where( 'name', $this->search )->get(),
]);
}
}
My blade template is resouces/views/livewire/search-accounts.blade.php and is as follows:
<div class="px-4 space-y-4 mt-8">
<form method="get">
<input class="border-solid border border-gray-300 p-2 w-full md:w-1/4"
type="text" placeholder="Search Accounts" wire:model="search"/>
</form>
<div wire:loading>Searching accounts...</div>
<div wire:loading.remove>
<!--
notice that $search is available as a public
variable, even though it's not part of the
data array
-->
#if ($search == "")
<div class="text-gray-500 text-sm">
Enter a term to search for accounts.
</div>
#else
#if($accounts->isEmpty())
<div class="text-gray-500 text-sm">
No matching result was found.
</div>
#else
#foreach($accounts as $account)
<div>
<h3 class="text-lg text-gray-900 text-bold">{{$account->name}}</h3>
<p class="text-gray-500 text-sm">{{$account->url}}</p>
<p class="text-gray-500">{{$account->ipaddress}}</p>
</div>
#endforeach
#endif
#endif
</div>
</div>
When I view my app in the browser the search bar is shown and contains a placeholder called Search Accounts. I click the search bar and start typing but no search is done (my browsers dev tools show no Network or console activity).
Have I missed something? I've tried php artisan livewire:discover and cleared caches. I have followed a tutorial (https://laravel-livewire.com/) and not sure why I'm not getting the expected results. Other posts like this are actually getting some network activity as the ajax requests are made which I'm not seeing at all.
thanks
Craig
Turned out that my #livewireScripts wasn't included in my master blade template. Once this was added livewire worked.
Try changing this
<input class="border-solid border border-gray-300 p-2 w-full md:w-1/4"
type="text" placeholder="Search Accounts" wire:model="search"/>
to
<input class="border-solid border border-gray-300 p-2 w-full md:w-1/4"
type="text" placeholder="Search Accounts" wire:model.debounce.500ms="search"/>
This should send a network request at most every 500ms after an event (helps to not hammer the backend with every keypress).
Have a look at this page of the docs https://laravel-livewire.com/docs/2.x/actions
Related
I have a blade. file with multiple livewire components:
<section class="">
<h2 id="page-goal">Add A New Item</h2>
#livewire('libraries.catalog-item-create', ['categories' => $categories, 'library' => $library])
#livewire('libraries.generic-publisher-create')
#livewire('libraries.generic-title-create')
#livewire('libraries.generic-artist-create')
#livewire('libraries.generic-tempo-create')
</section>
Each component is an input form that ends with a 'Next' button, ex:
<button
wire:click="nextStep()"
class="#if($next) bg-black text-white cursor-pointer #else bg-gray-500 text-gray-300 cursor-default #endif rounded w-20"
#if(! $next) disabled #endif >
Next
</button>
However, on the generic-artis-create form, I want to use the $emit format as follows:
<div class="flex flex-row">
<button
wire:click="$emit('nextStep','tempo')"
class="#if(count($artists) || ($artistObject && $artistTypeObject)) bg-black text-white cursor-pointer #else bg-gray-500 text-gray-300 cursor-default #endif rounded w-20"
#if(! $next) disabled #endif >
Next
</button>
</div>
I have a 'nextStep()' method in each Component which I use to advance the user through the components.
But, when I click the Next button with the direct $emit(), nothing happens, i.e. there's no Network activity. Livewire is brilliant, so I know I'm doing something wrong.
Any and all help is appreciated!
Sometimes asking the question leads to the answer. I was not completing the action which changed the state of the '$next' property, so the button was disabled. Fixing this allowed the $emit('name','value') to work.
Back again with another question I am hoping someone might have an idea for regarding the app that I am working on.
In this educational assessment application, I have assessments which have a one-to-many relationship with Competencies (similar to a Blog Post Category) and a many-to-many relationship with Contexts (similar to a Blog Tag).
I am trying to create a report view which will allow me to show a card for each of the Competency & Context combinations and then count all assessments where that competency & context combination exists (think of this as counting how many blog posts are in each category & tag combination, even if that number is 0).
So far I am able to produce the report which lists a card for each competency and context combination but I can't figure out how to pass that information to the controller for use in the query which will find the relevant assessments.
Here is my Report View
<x-app-layout title="{{ config('app.name', 'Laravel') }}">
<div class="container grid px-6 mx-auto">
<h2 class="my-6 text-2xl font-semibold text-gray-700 dark:text-gray-200">
{{ __('Reports') }}
</h2>
<div class="grid gap-6 mb-8 md:grid-cols-2 xl:grid-cols-4">
#foreach ($competencies as $competency) <br>
#foreach ($contexts as $context)
<div class="flex items-center p-4 bg-white rounded-lg shadow-xs dark:bg-gray-800">
<div>
<p class="text-lg font-semibold text-gray-700 dark:text-gray-200">
{{ $competency->name}}
</p>
<p class="mb-2 text-sm font-medium text-gray-600 dark:text-gray-400">
{{ $context->name }}
</p>
{{ $assessments }}
</div>
</div>
#endforeach
#endforeach
</div>
</x-app-layout>
Here is my Report Controller
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Assessment;
use App\Models\Competency;
use App\Models\Context;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
class ReportController extends Controller
{
public function index(Competency $competency, Context $context)
{
return view('dashboard.reports.index', [
'competencies' => Competency::where('team_id', Auth::user()->currentTeam->id)->get(),
'contexts' => Context::where('team_id', Auth::user()->currentTeam->id)->get(),
'assessments' => Assessment::where('competency_id', $competency->id)->whereRelation('contexts', 'context_id', $context->id)->count(),
]);
}
}
Here is an image of what this currently produced in the Report View: https://imgur.com/a/SQSf7UM
Please let me know if there is additional detail which would be helpful
it's better you declare the relation in the model, let's assume:
an assessment has one category,
an assessment has many contexts,
You can create an additional table with its model to store assessment contexts (because it assessent can has many contexts), let's say the table name is assessment_contexts and the model name is AssesmentContext. Or you can just run php artisan make:model AssessmentContext -m.
At least it has 2 columns, assessment_id and context_id,
Then inside AssessmentContext, add this function to create a simple relation,
public function assessment() {
return $this->hasOne(Assessment::class);
}
public function context() {
return $this->hasOne(Context::class);
}
Declare this functions inside the Assessment model,
public function competency() {
return $this->hasOne(Competency::class);
}
public function contexts() {
return $this->hasMany(AssesmentContext::class);
}
And add the this to the Competency model,
public function assessments() {
return $this->hasMany(Assessment::class);
}
And this for the Context model,
public function assessments() {
return $this->hasMany(AssessmentContext::class);
}
You have to add foreign key inside assessments table that refers to category primary key id, (I recommend you the column name is category_id).
Then finally in your controller, you can just declare the competencies list, and inside your view, you can access all the relate data. i.e:
return view('dashboard.reports.index', [
'competencies' => Competency::where('team_id', Auth::user()->currentTeam->id)->get()
]);
Inside view,
#foreach ($competencies as $competency) <br>
#foreach ($competency->assessments as $assessment)
<div class="flex items-center p-4 bg-white rounded-lg shadow-xs dark:bg-gray-800">
<div>
<p class="text-lg font-semibold text-gray-700 dark:text-gray-200">
{{ $competency->name}}
</p>
#foreach ($assessment->contexts as $context)
<p class="mb-2 text-sm font-medium text-gray-600 dark:text-gray-400">
{{ $context->name }}
</p>
#endforeach
</div>
</div>
#endforeach
#endforeach
This is just simple scheme of relation, there are other ways to do it that maybe better and more optimal.
I want to display multiple charts in a single page or different pages. How can I reuse the blade file instead of repeating/retyping the code?
I created a plain blade file _chart-widget.blade.php and I want the variable value to change depending on the page, or depending on what I want to set the variable in each <section> of a page
<!--begin::Charts Widget 1-->
<div class="card {{ $class ?? '' }}">
<!--begin::Header-->
<div class="card-header border-0 pt-5">
<!--begin::Title-->
<h3 class="card-title align-items-start flex-column">
<span class="card-label fw-bolder fs-3 mb-1">Recent Statistics</span>
<span class="text-muted fw-bold fs-7">More than 400 new members</span>
</h3>
<!--end::Title-->
<!--begin::Toolbar-->
<div class="card-toolbar">
<!--begin::Menu-->
<button type="button" class="btn btn-sm btn-icon btn-color-primary btn-active-light-primary" data-kt-menu-trigger="click" data-kt-menu-placement="bottom-end">
{!! theme()->getSvgIcon("icons/duotune/general/gen024.svg", "svg-icon-2") !!}
</button>
{{ theme()->getView('partials/menus/_menu-1') }}
<!--end::Menu-->
</div>
<!--end::Toolbar-->
</div>
<!--end::Header-->
<!--begin::Body-->
<div class="card-body">
<!--begin::Chart-->
<div id="kt_charts_widget_1_chart" style="height: 350px"></div>
<!--end::Chart-->
</div>
<!--end::Body-->
</div>
<!--end::Charts Widget 1-->
How can I make the code above dynamic and reusable when I #include it?
You can include views in Laravel blade template.
here you can read more.
Just use like this:
<div>
#include('_chart-widget')
</div>
If you need to pass data to your widget component, just give parameters as an array to your component:
#include('view.name', ['status' => 'complete'])
If you want variables to be different in each page simply pass vairables from Controller.If you are on the same page and including same blade multiple times this can help you:
#include('view.name', ['code' => 'complete'])
This will set different values for $code variable in different sections.
Check out documentation here.
Description
I am type hinting model properties and trying to delete the invitation data. Below is the error which its throwing me back. Please help me with it, as I am unable to spot what is it that I'm missing.
Typed property App\Http\Livewire\Backend\UserManagement\FormComponent\InvitationManagementModal::$invitation must not be accessed before initialization
Stripped-down, copy-pastable code snippets
Livewire\InvitationManagementModal.php
<?php
namespace App\Http\Livewire\Backend\UserManagement\FormComponent;
use Livewire\Component;
use Livewire\WithPagination;
use App\Http\Livewire\Backend\DataTable\WithCachedRows;
use App\Models\Invitation;
class InvitationManagementModal extends Component
{
use WithPagination, WithCachedRows;
public $showInvitationManagementModal = false;
public Invitation $invitation;
protected $listeners = ['manageInvitation'];
public function manageInvitation()
{
$this->showInvitationManagementModal = true;
}
public function deleteInvitation(Invitation $invitation)
{
$this->invitation->delete();
}
public function getInvitationRowsProperty()
{
return $this->cache(function () {
$invitations = Invitation::where('registered_at', null)->paginate(5);
return $invitations;
});
}
public function render()
{
return view('livewire.backend.user-management.form-component.invitation-management-modal', ['invitations' => $this->invitationRows]);
}
}
livewire\invitation-management-modal.blade.php
<div>
<x-modal.stacked wire:model.defer="showInvitationManagementModal" id="scroll-lock">
<x-slot name="title">Manage Invitation</x-slot>
<x-slot name="description">Manage all the invitations which are yet to be accepted.</x-slot>
<x-slot name="content">
<div class="p-8 space-y-4">
<ul class="flex flex-col divide divide-y w-full bg-white rounded-lg shadow">
#forelse($invitations as $key => $invitation)
<li class="flex flex-row">
<div class="flex flex-1 items-center px-8 py-4">
<div class="flex-1 mr-16">
<div class="text-sm dark:text-white">
{{ $invitation->email }}
</div>
</div>
<button wire:click="deleteInvitation" class="text-right flex justify-end">
<x-icon.trash />
</button>
</div>
</li>
#empty
#endforelse
</ul>
<div>
{{ $invitations->links() }}
</div>
</div>
</x-slot>
<x-slot name="footer">
<x-button.secondary wire:click.defer="$set('showInvitationManagementModal', false)">Cancel</x-button.secondary>
</x-slot>
</x-modal.stacked>
</div>
Context
Livewire version: 2.3.5
Laravel version: 8.20.1
Alpine version: 2.8.0
Browser: Chrome
The other answers here both have some minor things to note about them. You don't have to check $invitation, because the typehinting Invitation makes Laravel use Model-Route-Binding, which fetches the corresponding record - or throws a HTTP 404 status code if not found.
Secondly, and this is the actual error you are currently seeing yourself, is that you don't have to do anything to the $this->invitation, since its not set. You should instead pass a parameter to the method.
When looping data in Livewire, it is always recommended to use wire:key, so that Livewire can keep track of each record in the loop.
So for the actual delete method, just call the delete method on the input-variable.
public function deleteInvitation(Invitation $invitation)
{
$invitation->delete();
// Emit an event to notify the user that the record was deleted
// Refresh the parent component to remove the invitation from the list
}
For your blade, add wire:key to the first element in the loop and pass the ID to the method.
(so wire:click="deleteInvitation({{ $invitation->id }})" instead of wire:click="deleteInvitation").
#forelse($invitations as $key => $invitation)
<li class="flex flex-row" wire:key="invitation_{{ $invitation->id }}">
<div class="flex flex-1 items-center px-8 py-4">
<div class="flex-1 mr-16">
<div class="text-sm dark:text-white">
{{ $invitation->email }}
</div>
</div>
<button wire:click="deleteInvitation({{ $invitation->id }})" class="text-right flex justify-end">
<x-icon.trash />
</button>
</div>
</li>
#empty
#endforelse
This in turn means that, since its never used, you can remove the declaration of the $invitation property of that class, the line just after public $showInvitationManagementModal = false;.
public Invitation $invitation;
Try this:
public function deleteInvitation(Invitation $invitation)
{
$this->invitation = $invitation;
$this->invitation->delete();
}
In my application, I've always been able to pass data to any view as one would normally do using view('myView', compact('data'));. As of today, any view I try to render this way times out. I'm getting the error Maximum execution time of 120 seconds exceeded in Whoops!. I tried increasing php.ini and httpd.conf timeout times but no cigar. It's really odd and it doesn't make sense to me because I've always been able to render my views almost instantly, even when retrieving 15k+ records from the database and passing them to the view like I've always done.
My controller:
use App\Product;
use Illuminate\Support\Facades\Session;
class HomeController extends Controller {
public function __construct()
{
$this->middleware('auth');
}
public function index()
{
//the controller is normally like this
//$products = Product::paginate(16);
//return view('home', compact('products'));
//I'm testing with these 2 lines below but no cigar.
$product = Product::wherePid(303)->first();
return view('test', compact('product'));
}
}
The test view I created:
#extends('app')
#section('content')
{{ $product->name }}
#stop
My application view:
#extends('app')
<pre>{{ var_dump(Session::all())}}</pre>
#section('content')
<div class="row">
#foreach($products as $product)
<div class="col-xs-6 col-sm-3 col-lg-3 col-md-3">
<?php
if($product->img[7] == 'm' || $product->img[7] == 'M') echo "<div class='continenteIcon'></div>";
else echo "<div class='jumboIcon'></div>";
?>
<div class="thumbnail">
<a href="products/{{$product->pid}}"><img src="{{$product->img}}" title="
<?php
if($product->dispname != '') echo $product->dispname;
else echo $product->name;
?> ">
</a>
<div class="caption">
<h4>
<a style="text-decoration:none;" class="wordwrap" title="
<?php
if($product->dispname != '')
echo $product->dispname;
else echo $product->name;
?>" href="products/{{$product->pid}}">
<?php
if($product->dispname != '')
echo $product->dispname;
else echo $product->name;?>
</a>
</h4>
<p>{{$product->brand}}</p>
<span class="pull-right price">€{{$product->price}}</span>
<br/>
<span class="pull-right ppk">€{{round($product->pricekilo, 2)}} Kg, L ou Und</span>
</div>
<div class="ratings">
<p class="pull-right"> {{-- # review--}}</p>
<p>
<form method="post" action="add/{{$product->pid}}">
<input type="hidden" name="_token" value="{{{ csrf_token() }}}" />
<button title="Adicionar ao carrinho" type="submit" class="btn btn-success">
<i class="fa fa-shopping-cart"></i>
</button>
</form>
<form method="post" action="products/related/{{$product->pid}}">
<input type="hidden" name="_token" value="{{{ csrf_token() }}}" />
<button title="Ver artigos semelhantes" style="position:relative; bottom:35px;" type="submit" class="btn btn-info pull-right">
<i class="fa fa-search"></i>
</button>
</form>
</p>
</div>
</div>
</div>
#endforeach
</div>
<div class="row">
{!! $products->render() !!}
</div>
<div class="row">
<div class="pull-right">
* Preço por unidade, Litro ou Kilograma
</div>
#stop
#section('scripts')
#stop
The problem doesn't only happen in this view, but every single time I try to fecth someting from the database and pass it to the view to render. I keep getting timeouts and I can't seem to fix it no matter what I do.
I am clueless why this is happening. It seems like it started out of the blue. I have no Idea what could be causing this issue.
Any help?
P.S.: I'm using Wamp.
EDIT: I forgot to add something that might be important:
Everything is up and running in Wamp. If I dd() out the query result and do not render the view
$products = Product::paginate(16);
dd($products);
//return view('home', compact('products'));
this is fast, as it always used to be. And by fast I mean it takes less than 1 second to retrieve everything I need. But if I render the view with
return view('home', compact('products'));
everything just stalls and I get a 500 (I checked with Fiddler2 and after the page stops loading, the request status is 500)
It seems like you may be requesting too many records which may be using too much of your RAM. I would use the chunk command to help you with managing the amount you're requesting.
For example:
User::chunk(200, function($users)
{
foreach ($users as $user)
{
//
}
});
First check logs.
Next try to dd($product)
Next if you try to render view with last 2 lines (getting first record) remove pagination from template.
Clean template to minimum e.g.
#extends('app')
#section('content')
<div class="row">
#foreach($products as $product)
#endforeach
</div>
#stop
I just sorted it out. The issue was in the following block of code in app.blade.php.
$size = Session::get('size');
...
<input type="text" value="'.Session::get($item).'">
...
I was messing around with data from an existing session and everything was working fine. I assumed I was doing it right. I wasn't. Not by a chance :)
Assumption is the mother of all screw ups.
Surrounded the whole block with if(Session::has('size') and everything is blazing fast and running smoothly as usual.
Thanks #Pyton for pointing me out into the right direction and thanks everyone for your contribution.