Laravel Livewire live search not correctly updating results - php

I have the following livewire code to perform a search of page titles which works fine on the first search, but subsequent searches do not completely remove the results from the previous search.
namespace App\Http\Livewire\Page;
use App\Models\Page;
use Livewire\Component;
class Search extends Component
{
public $searchQuery = "";
public $searchResults;
public function resetSearchValue() {
$this->searchQuery = "";
}
public function mount() {
$this->reset();
}
public function render()
{
if(strlen($this->searchQuery) > 2) {
$this->searchResults = Page::query()
->select(['title', 'slug'])
->where('title', 'like', "%{$this->searchQuery}%")
->get();
}
return view('livewire.page.search');
}
}
and
<div>
<input
wire:model.debounce.350ms="searchQuery"
#focus="searchFocused = true"
#blur="searchFocused = false"
#click.outside="$wire.resetSearchValue()"
autocomplete="off"
type="text"
placeholder="Search..."
id="sitesearch"
>
#if(strlen($searchQuery) > 0)
<div
role="menu"
aria-orientation="vertical"
-labelledby="menu-button"
tabindex="-1"
>
<div class="py-1" role="none">
#if(strlen($searchQuery) > 2)
#if($searchResults->count() > 0)
#foreach($searchResults as $result)
<a
href="{{ route('pages.index', $result->slug) }}"
role="menuitem"
tabindex="-1"
id="menu-item-0"
>{{ $result->title }}</a>
#endforeach
#else
<p>
No results found
</p>
#endif
#else
<p>
You need to type at least 3 characters
</p>
</div>
</div>
#endif
</div>
Searching for "Page" for example will return something like
My Page 1
Page 2
Wonderful Page 3
But if I then search for "Page Z" the results I get are
My Page 1
Page 2
No results found
If I the search for "Page Zz" the results I get are
My Page 1
No results found
I can't work out why it's not clearing the results from previous results.

Basically, this is the result of a DOM-diffing issue Livewire has when it can't keep track of elements, typically dynamic elements generated in a loop.
The simple solution to this, is to add wire:key with a value to the root element in your loop, like shown below.
#if(strlen($searchQuery) > 2)
#if($searchResults->count() > 0)
#foreach($searchResults as $result)
<a
href="{{ route('pages.index', $result->slug) }}"
role="menuitem"
tabindex="-1"
id="menu-item-0"
wire:key="result-{{ $result->id }}"
>{{ $result->title }}</a>
#endforeach
#else
<p wire:key="no-results">
No results found
</p>
#endif
#else
<p wire:key="searchquery-short">
You need to type at least 3 characters
</p>
#endif
Also, I've added these to the other options which may be shown in place, just so it's no doubt about which element it should show.
Just a note, all the values to wire:key on a page must be unique (like with ID attributes to HTML elements).

Related

How to exclude certain styling in page2 in pagination

I have followed Jeffery Way in Laravel 8 from scratch amazing series but I'm having a trouble in pagination. In the index page we're making our latest post to have a specific styling and then first 2 posts comes after with a different styling and then rest of posts in different styling. The problem is that these styles are carrying over with us to page2 and 3 and ...etc
I want to make this to only posts in page1 or homepage and when I go to page 2 I should have a default styling for all posts.
This is the index
<x-bloglayout>
#include('posts.__header')
#if ($posts->count())
<x-featuredCard :post="$posts[0]" />
#if ($posts->count() > 1)
<div class="lg:grid lg:grid-cols-2">
<x-postCard :post="$posts[1]" />
<x-postCard :post="$posts[2]" />
</div>
<div class="lg:grid lg:grid-cols-3">
#foreach ($posts->skip(3) as $post)
<x-postCard :post="$post" />
#endforeach
</div>
#endif
{{ $posts->links() }}
#else
<p class="text-center">No posts matches your search, please check back later</p>
#endif
</x-bloglayout>
I tried to use if directive to say if route is home or ?/page1 do this if not do that but it doesn't seem to work.
This is my pagination:
public function index()
{
return view('posts.index', [
'posts' => Post::latest()->filter(request(['search', 'category', 'author']))->paginate(6)->withQueryString(),
]);
}
Thanks
You can use currentPage() method on paginator and check if it's on first page. May not be fanciest way but here is an example:
<x-bloglayout>
#include('posts.__header')
#if ($posts->count())
<x-featuredCard :post="$posts[0]" />
#if ($posts->count() > 1)
#if($posts->currentPage() == 1)
<div class="lg:grid lg:grid-cols-2">
<x-postCard :post="$posts[1]" />
<x-postCard :post="$posts[2]" />
</div>
#endif
<div class="lg:grid lg:grid-cols-3">
#foreach ($posts->skip($posts->currentPage() == 1 ? 3 : 0) as $post)
<x-postCard :post="$post" />
#endforeach
</div>
#endif
{{ $posts->links() }}
#else
<p class="text-center">No posts matches your search, please check back later</p>
#endif
</x-bloglayout>
You can check Paginator methods from Laravel 8.x document: https://laravel.com/docs/8.x/pagination#paginator-instance-methods

Cannot find the way to make #if statement works in blade

I'm making a "teacher's" app, and I want to make a log-in page which changes depending if there's registered users in the database or not.
I want to make a redirection button to a create user page if there aren't auth users in database, and to make a select user view if the database have one or more users.
The problem is that I don't know how to exactly do this, 'cause the view always shows me the first statement (what I've got in the if), also if in the database are registered users. Can anyone help me with this please?
This is the blade file:
#if (empty(Auth::user()->id))
<div class="grid-item" id="grid-item5">
<div id="title">
<h1>Welcome</h1>
<p>We see there aren't users</p>
</div>
<div id="loginForm">
<button type="button" onclick="window.location='{{ url("/newUser") }}'">Button</button>
</div>
</div>
#else
<div class="grid-item" id="grid-item5">
<div id="title">
<h1>Select an user</h1>
</div>
<div id="loginForm"></div>
</div>
#endif
Here you have the controller index method:
public function index()
{
$users = User::all();
return view('/', compact('users'));
}
And finally here you have the page:
The following code is the sample for it, kindly replace code accordingly
#if(!$user)
//show button
#else
//dont show button
#endif
I think your question is you want to check if there is user in database.
So no need to check if the user authenticated but to check if there is user on the database.
In your controller
public function index() {
return view('/', ['users' => User::all()]);
}
and in your blade file
#if(!$users)
<div class="grid-item" id="grid-item5">
<div id="title">
<h1>Welcome</h1>
<p>We see there aren't users</p>
</div>
<div id="loginForm">
<button type="button" onclick="window.location='{{ url("/newUser") }}'">Button</button>
</div>
</div>
#else
<div class="grid-item" id="grid-item5">
<div id="title">
<h1>Select an user</h1>
</div>
<div id="loginForm"></div>
</div>
#endif
This function will get the current authenticated user: Auth::user(). I guess what you are trying to achieve is #if(empty($users)) where $users is the variable you are passing on controller.
If you want to verify if the user that accessed to that view is authenticated you can simply use #auth and #guest.
Also i would suggest you to change your button to an <a> tag and your href would be <a href="{{ route('route.name') }}" where route.name would be defined in your routes file.
in your controller:
you can create a folder inside views called users and then the index.blade.php (views/users/index.blade.php)
public function index()
{
$users = Users::all();
return view('users.index')->with('users', $users);
}
in your view:
#if(count($users) < 1)
...
#else
...
#endif
count is validating if the $users array length is less then 1 (in other words if the array is empty).
Alternative you can you isEmpty()
#if($users->isEmpty())
...
#else
...
#endif

Object of class Illuminate\Database\Eloquent\Collection could not be converted to int Laravel 5.4

I have an old database filled with info and now i want to display Category names from that DB and getting this error.
Here is my controller
public function forums(){
$cats = Forum_cats::all();
return view ('lapas.forums.index')->with('cats', $cats);
}
}
here is my view
#if(count($cats >1))
#foreach($cats as $cati)
<div class = "well">
<h3>{{$cati->description}}</hr>
</div>
#endforeach
#else
#endif
and here is screen of DB structure
http://prntscr.com/mg5nk1
Ask for more info if needed!
It looks like your operator is misplaced:
#if(count($cats) > 1)
#foreach($cats as $cati)
<div class = "well">
<h3>{{$cati->description}}</hr>
</div>
#endforeach
#else
#endif
It looks like you're trying to loop through these $cats in a Blade template. You might also try forelse:
#forelse($cats as $cati)
<div class = "well">
<h3>{{$cati->description}}</hr>
</div>
#empty
{{-- Action if there are none --}}
#endforelse
Edit: Docs here: https://laravel.com/docs/5.4/blade#loops
Your if is wrong. Try :
#if($cats->count() > 1)

Laravel 5 times out when rendering any view with data passed from controller

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.

laravel blade template variables

In working with laravel blade templates, what's the approved way to manage variables in output?
For example, I'm working on a view that shows upcoming chores / tasks for each farmer. The pivot table holds a due_at datetime field for the task, and I'd like to change the class of the item depending on whether it's overdue, done, etc.
#foreach ($farmer->tasks as $task)
#if ($task->pivot->due_at) < date(now))
$style = 'alert alert-danger';
#elseif ($task->pivot->due_at) > date(now))
$style = 'alert alert-success';
#else
$style = '';
#endif
<div class="list-group-item {{ $style }}">{{$task->name}} <span class="glyphicon glyphicon-calendar"> {{ $task->pivot->due_at }}</span> <span class="glyphicon glyphicon-pencil"></span><span class="glyphicon glyphicon-trash"></span></div>
#endforeach
This example throws an error: Undefined variable: style (View: /home/vagrant/Code/app/views/farmers/show.blade.php)
I don't see an obvious way to do simple code blocks to set variables like I'd do in a "normal" PHP view to define the class to apply to the task item by doing some basic calculations on the due_at value.
Should this logic be moved to a helper function or something?
Assume due_at is a timestamp.
#foreach ($farmer->tasks as $task)
#if (Carbon::parse($task->pivot->due_at) < Carbon::now())
<?php $style = 'alert alert-danger'; ?>
#elseif (Carbon::parse($task->pivot->due_at) > Carbon::now())
<?php $style = 'alert alert-success'; ?>
#else
<?php $style = ''; ?>
#endif
<div class="list-group-item {{ $style }}">{{$task->name}} <span class="glyphicon glyphicon-calendar"> {{ $task->pivot->due_at }}</span> <span class="glyphicon glyphicon-pencil"></span><span class="glyphicon glyphicon-trash"></span></div>
#endforeach
#Anam Your answer works, but I will use following method.
#user101289 Assuming that you have default layout and it yields content section. So to declare and use variables I would suggest you use vars section in your inner template file and declare all your variables all at once on the top. And then use it. Since we will not yield the section vars, it will not going to print it.
This will help you to keep track of variables used and its standard method to declare all variables on top and use in rest of the program:
#extends('layouts.default') /* Your default layout template file. */
#section("vars")
{{ $yourVar = 'Your value' }}
#endsection
#section("content") /* The content section which we will print */
// Your other HTML and FORM code and you can use variables defined in **vars** section
#endsection
#stop

Categories