Laravel stop render Livewire before length is less than 2 characters - laravel

I have an input in Laraval Livewire for searching in a model and show the results immediately.
I want 2 way data binding. so I've used wire:model:
<input type="text" wire:model="query">
but the problem is I want to start sending requests after the user entered the third character.
I know we have wire:model.debounce.1000ms but it's not useful for me. because it will start sending a request after 1000 millisecond that we stopped typing.
I know we have wire:model.lazy but it will start sending a request after we clicked another place or unfocused from the current element.

UPDATED
As I mentioned before it is better to use a submit button for that action
another way which I didn't tried it yet is , (try one of them)
class SearchPosts extends Component
{
public $search = '';
protected $queryString = [
'search' => ['except' => fn() => count($this->search) < 3],
//'search' => ['except' => fn($value) => count($value) < 3],
];
public function render()
{
return view('livewire.search-posts', [
'posts' => Post::where('title', 'like', '%'.$this->search.'%')->get(),
]);
}
}
Component
<div>
<input wire:model.debounce="search" type="search" placeholder="Search posts by title...">
<h1>Search Results:</h1>
<ul>
#foreach($posts as $post)
<li>{{ $post->title }}</li>
#endforeach
</ul>
</div>

Related

laravel pagination with search wrong links

I am trying to apply laravel pagination with search functionality.
So far I have tried both live search with laravel pagination and that keeps failing and normal search with pagination where I got better results but still not working.
Here is what I last tried for normal search with pagination:
ProjectController:
public function index()
{
$projects = Project::paginate(6);
return view('dashboard.projects.index',['projects' => $projects]);
}
public function search(Request $request)
{
$q = $request->input('q');
if(!empty($q)){
//dd("search");
$projects = Project::where('name', 'LIKE', '%' . $q . "%")->orderBy('created_at','desc')->paginate(6)->setPath('');
$projects->appends(['q' => $request->input('q')]);
return view ( 'dashboard.projects.index')->with('projects',$projects);
}
return redirect('/dashboard/projects/');
}
dashboard.projects.index
<div class="container-fluid">
<div class="row section-primary">
#foreach($projects as $project)
<div class="col-12 col-md-4 mb-2">
<div class="card project h-100">
<div class="card-header">
<h4>{{$project->name}}</h4>
</div>
<div class="card-body">
<p><strong>Project Manager:</strong> {{$project->manager_name}}</p>
<p class="description">{{ substr($project->description, 0, 110) }}...</p>
<div class="display-flex">
<div>View</div>
<div>Edit</div>
<div>Members</div>
<div>
{!!Form::open(['action' => ['ProjectController#destroy', $project->id], 'method' => 'POST','class' => 'form-link'])!!}
{{Form::hidden('_method', 'DELETE')}}
{{Form::submit('Delete', ['class' => 'button-link'])}}
{!!Form::close()!!}
</div>
</div>
</div>
</div>
</div>
#endforeach
</div>
</div>
<div>
{!! $projects->links()!!}
</div>
The code works fine when I go to dashboard/projects and pagination works.
The search works but pagination fails.
When I press the search button it first goes to dashboard/projects/search
Then when I press page 2 button on pagination it goes to dashboard/projects/search?q=project&page=2, here it is requesting the show function which is unrelated.
PS: Any extra help for live search with pagination would be amazing.
I have managed to fix the problem, for anyone who is facing the same issue, I used in my routes:
Route::resource('/dashboard/projects', 'ProjectController');
Route::any('/dashboard/projects/search', 'ProjectController#search');
For some reasons, /dashboard/projects/search was not a good idea as it was going to the show defined by resource, all I had to do is change:
Route::any('/dashboard/projects/search', 'ProjectController#search');
to
Route::any('/dashboard/project/search', 'ProjectController#search');
so they look like different routes until I find/learn a better solution.
The other solutions is putting the search route first such as:
Route::any('/dashboard/projects/search', 'ProjectController#search');
Route::resource('/dashboard/projects', 'ProjectController');
In larvel => 6 Version:
Input no longer exists In larvel 6,7,8 Version. Use Request instead of Input.
Change $q = $request->input('q'); to $q = $request->q;
Your ProjectController:
use Illuminate\Support\Facades\Request;
..
..
public function search(Request $request)
{
$q = $request->q;
if(!empty($q)){
//dd("search");
$projects = Project::where('name', 'LIKE', '%' . $q . "%")->orderBy('created_at','desc')->paginate(6)->setPath('');
$projects->appends(['q' => $request->input('q')]);
return view ( 'dashboard.projects.index')->with('projects',$projects);
}
return redirect('/dashboard/projects/');
}

Laravel use sync on a many to many that includes a multiple extra fields

I'm trying to use sync on a many to many that includes a status and a comment. I can sync the applications without status and comment just fine.
NewUserAccount Model
public function applications()
{
return $this->belongsToMany('App\Application', 'new_user_account_applications', 'new_user_id')->withPivot('application_comment', 'status');
}
Application Model
public function newUserAccounts()
{
return $this->belongsToMany('App\NewUserAccount', 'new_user_accounts_applications', 'new_user_id')->withPivot('application_comment', 'status');
}
My NewUserAccountController
public function store(StoreRequest $request)
{
$userAccount = NewUserAccount::create(array_merge(
$request->all(),
['submitted_by' => $requester->id],
['start_date' => Carbon::parse($request->input('start_date'))],
['account_expires' => $request->accountExpires('newAccountExpireDate')],
['company_id' => $requester->company_id],
['username' => $request->manuallyAssignId()]
));
// Here I sync applications and include application comment and status
$userAccount->applications()->sync($request->applications, ['application_comment' => $request->application_comment, 'status' => 0]);
....
}
My pivot showing status and comment correctly
My form. Here is where I'm not sure how to handle the comment and get it to save with each application pivot record.
#foreach($applications as $application)
<label class="k-checkbox">
<input value="{{ $application->id }}" name="applications[]" type="checkbox">{{ $application->application_name }} <span></span>
</label>
<div class="form-group col-lg-4 mb-3">
<label>Comments</label>
<textarea name="application_comment[]" class="form-control" rows="2"></textarea>
</div>
#endforeach
First, you need to set the correct index for the application_comment attribute in your textarea. It's needed to correctly determine the comment for each application.
#foreach($applications as $application)
...
<textarea name="application_comment[{{ $application->id }}]" class="form-control" rows="2"></textarea>
...
#endforeach
Then, you just need to format your data to:
$userAccount->applications()->sync([
application_id_1 => ['application_comment' => 'comment for application_id 1'],
application_id_2 => ['application_comment' => 'comment for application_id 2'],
...
]);
So, here it is
$applications = collect($request->applications)->mapWithKeys(function ($appId) use ($request) {
return [$appId => [
'application_comment' => $request->input('application_comment')[$appId],
'status' => 0,
]];
});
$userAccount->applications()->sync($applications);

Flash messages display and sometimes they don't

I'm using a spatie/laravel-flash for displaying some flash messages. It works when I use simple HTML forms, but when I use Vue.js templates, the message doesn't show. (and sometimes they don't) after submitting the form and go to the next request.
main layout
<div class="col-lg-12 mb-2">
#include('layouts.partials.flash_message')
</div>
<section class="py-5">
#yield('content')
</section>
layouts.partials.flash_message
#if(flash()->message)
<div class="{{ flash()->class }} alert-dismissible" role="alert">
<button type="button" class="close close-white" data-dismiss="alert">×</button>
{{ flash()->message }}
</div>
#endif
create.blade.php
#extends('layouts.main_layout')
#section('content')
<create-school> </create-school>
#endsection
Vue.js template store() method
store()
{
axios.post('/master/schools', {
name: this.name,
}.then((response) => {
this.name = ''
}));
}
Laravel store method
<?php
public function store(Request $request)
{
$school = School::create([
'name' => $request->name,
]);
flash('success message', 'alert alert-success');
return back();
}
Since you are using ajax to store your data, you will need to provide the message as a response in that request. So basically the store method on your controller would look like
public function store(Request $request)
{
$school = School::create([
'name' => $request->name,
]);
return response($school, 201);
}
201 is the HTTP code to indicate a resource was created, and the by the RFC you should return the resource in the response.
In your Vue file, the store method would then be
store()
{
axios.post('/master/schools', {
name: this.name,
}.then((response) => {
this.name = ''
// Modify your DOM or alert the user
alert('Resource created')
}));
}
As a recomendation, you should aways validate user input before storing.

Validation error in Laravel - $errors array does not get populated after the validation failure

I've ran into a strange issue regarding validations in Laravel 5.2. I reviewed following questions on StackOverflow, but none of them seems to apply to my case:
Laravel validation not showing errors
Laravel Validation not returning error
The thing is, that I am trying to validate a title field, before persisting the Card object into the database. When I submit the form with an empty title field, as expected, It doesn't pass the validations. However, the $errors array doesn't get populated upon failure of the mentioned validations. Can anybody explain where am I going wrong with this code?
/////////////////////// CONTROLLER /////////////////////
public function create(Request $request)
{
$this->validate($request, [
'title' => 'required|min:10'
]);
Card::create($request->all());
return back();
}
///////////////////////// VIEW /////////////////////////
// Show errors, if any. (never gets triggered)
#if(count($errors))
<ul>
#foreach($errors->all() as $error)
<li>{{ $error }}</li>
#endforeach
</ul>
#endif
<form method="POST" action="/cards">
{{ csrf_field() }}
<div class="form-group">
// The textarea does not get populated with the 'old' value as well
<textarea class="form-control" name="title">{{ old('title') }}</textarea>
</div>
<div class="form-group">
<button class="btn btn-primary" type="submit">Add Card</button>
</div>
</form>
If you are running Laravel 5.2.27 and up, you no longer need to use the web middleware group. In fact, you shouldn't add it to your routes because it's now automatically applied by default.
If you open up your app/Http/RouteServiceProvider.php file, you will see this bit of code:
protected function mapWebRoutes(Router $router)
{
$router->group([
'namespace' => $this->namespace, 'middleware' => 'web',
], function ($router) {
require app_path('Http/routes.php');
});
}
Source: https://github.com/laravel/laravel/blob/master/app/Providers/RouteServiceProvider.php#L53
As you can see, it's automatically applying the web middleware for you. If you try to apply it again (more than once) in your routes file, you'll run into weird problems like what you are currently facing.
In order to find out the version of Laravel that you are running, run this command: php artisan --version
I guess you have to set the if clause to #if(count($errors) > 0)
In your controller, try adding a $validator->fails() statement, and using ->withErrors() to return any errors to your template.
public function create(Request $request)
{
$validator = Validator::make($request->all(), [
'title' => 'required|min:10'
]);
if ($validator->fails()) {
return back()->withErrors($validator);
}
Card::create($request->all());
return back();
}

Laravel 5.1 Form Request Validation Returns input

I am wondering if anyone knows how I can do this.
Currently, I am using form request validation so my store method looks something like
public function store(ProfileStore $request)
{
// do stuff.
Input::flush();
return redirect()->back();
}
^ Note the input flush, I don't want certain input stored as "old input" or passed back to the form so I am flushing it.
and then in my ProfileStore I have a some basic validation (eg.
public function rules()
{
return [
'title' => 'required|max:255',
'body' => 'required',
];
}
The problem is when I use Request Validation, its passing the the input back into the form along with the error messages. I have tried flushing input from the validation file, but doesn't work.
If I manually create a validator from my store method and not use Request Validation it works fine and will not pass back input.
Update:
So I am using Laravel Collective Forms & HTML, I think its related to that. Which is weird because I am using Form::open and as far as I know only Form model binding should be doing this on Form::model..
If I remove
Form::text('testfield', null);
and replace with standard input
<input tpye="text" name="testfield" value="">
No input is returned after validation which is correct. However when using Form::input values are returned from validation.
{!! Form::open(['route' => ['frontend.account.profile.save'], 'id' => 'profile-update', 'class' => 'profile', 'role' => 'form']) !!}
<div class="form-body">
<div class="row" id="billing_details_div">
<div class="col-xs-12">
{{-- Input gets passed back after failed validation when using Form::text() --}}
Form::text('testfield', null);
{{-- No Input gets passed back when using stantard HTML input
<input tpye="text" name="testfield" value=""> --}}
</div>
<div class="col-md-12 text-center">
<button type="submit" class="btn btn-lg btn-success"><span class="glyphicon glyphicon-floppy-disk"></span> Update Profile</button>
</div>
</div>
</div>
{!! Form::close() !!}
Any ideas?
Write this function in your ProfileStore request class and it should fix that.
public function response(array $errors)
{
if ($this->ajax() || $this->wantsJson()) {
return new JsonResponse($errors, 422);
}
return $this->redirector->to($this->getRedirectUrl())
->withErrors($errors, $this->errorBag);
}
I'm not quite sure if it works, so let me know if it does. ;)
Update
Try this as well
protected $dontFlash = ['password', 'password_confirmation', 'your-other-inputs'];

Resources