Laravel Livewire wire:click not trigger function defined - php

I am building a search bar within a webpage. Ideally, user would enter the search text in the search field, and then if there are records found, a search results table would show and display the record found. I am using Laravel Livewire to implement this feature, however, I ran into the problem that the wire:click not firing the event, and any help would be needed!
This is my blade file (resources/livewire/dashboard.blade.php) contains the search bar:
<form>
<label for="searchText" class="block text-xx font-medium text-gray-700">Search Users</label>
<div class="mt-1 flex rounded-md shadow-sm">
<div class="relative flex items-stretch flex-grow focus-within:z-10">
<input type="text" name="searchText" id="searchText"
class="focus:ring-indigo-500 focus:border-indigo-500 block w-full rounded-none rounded-l-md pl-10 sm:text-sm border-gray-300" placeholder="User ID / Email Address / Mobile Number"
wire:model="searchText">
</div>
<button wire:click="search()" class="-ml-px relative inline-flex items-center space-x-2 px-4 py-2 border border-gray-300 text-sm font-medium rounded-r-md text-gray-700 bg-gray-50 hover:bg-gray-100 focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500">
Search
</button>
</div>
</form>
and this is the action defined in the App/Http/Livewire/Dashboard.php file
<?php
namespace App\Http\Livewire;
use Illuminate\Support\Facades\Http;
use Livewire\Component;
class Dashboard extends Component
{
public $stats, $searchText;
public $showResultsTable = false;
protected $accountAPIRootURL = 'https://example.com/api/v2/';
public function render()
{
$response = Http::withHeaders([
'Accept' => 'application/json'
])->get($this->accountAPIRootURL . 'statistics/overview');
if ($response->successful()) {
$stats = $response['data'];
} else {
$stats = [
'total_users' => 0,
'new_users' => 0,
'invitations' => 0,
'new_invitations' => 0,
'requests' => 0,
'new_requests' => 0
];
}
$this->stats = $stats;
$this->searchText = '';
return view('livewire.dashboard');
}
public function search()
{
$response = Http::withHeaders([
'Accept' => 'application'
])->get($this->accountAPIRootURL . 'admin/search', [
'searchText' => $this->searchText
]);
if ($response->successful()) {
$this->showResultsTable = true;
$this->searchText = '';
}
}
}
This is my template.blade.php file, where the #livewire component is called
#extends('layouts.app')
#section('content')
#livewire('dashboard')
#endsection
I am not worrying too much about displaying the result table now because it seems like the search() function is not being triggered when I click on the Search button within the blade. How do I know that, I put a dd() within the search() function and it is not being executed.
I would appreciate any help!

You don't need to use the parenthesis, wire:click="search"
UPDATE: Try this different syntax while you are handle a form in livewire
<form wire:submit.prevent="search">
//.....
<div class="mt-1 flex rounded-md shadow-sm">
//.....
<button class="-ml-px relative inline-flex items-center space-x-2 px-4 py-2 border border-gray-300 text-sm font-medium rounded-r-md text-gray-700 bg-gray-50 hover:bg-gray-100 focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500">
Search
</button>
</div>
</form>

Related

Image uploaded but database not updated (Update data with image in Laravel 8)

I have make feature to update profile. The table is User HasOne DetailUser so every single user have 1 detail user. Everything works fine, except the photo update.
When i was trying to update the photo, photo was succesfully update into storage in public but the database not updated. Maybe somebody could help me. Thanks...
Controller
public function update(UpdateProfileRequest $request_profile, UpdateDetailUserRequest $request_detail_user)
{
$data_profile = $request_profile->all();
$data_detail_user = $request_detail_user->all();
// save to user
$user = User::find(Auth::user()->id);
$user = User::where('id', Auth::user()->id)->first();
$user->name = $request_profile->input('name');
// save to detail user
if($user->save()) {
$detail_user = DetailUser::find($user->detail_user->id);
$detail_user = DetailUser::where('id', $user->detail_user->id)->first();
// delete old file from storage
if (isset($data_detail_user['photo'])) {
$data = 'storage/'. $detail_user['photo'];
if (Storage::exists($data)) {
Storage::delete($data);
} else {
Storage::delete('storage/app/public'. $detail_user['photo']);
}
}
// store file to storage
if (isset($data_detail_user['photo'])) {
$data_detail_user['photo'] = $request_detail_user->file('photo')->store(
'assets/photo', 'public'
);
}
$detail_user->role = $request_detail_user->input('role');
$detail_user->contact_number = $request_detail_user->input('contact_number');
$detail_user->biography = $request_detail_user->input('biography');
$detail_user->save();
}
// save to experience
$experience_user_id = ExperienceUser::where('detail_user_id', $detail_user['id'])->first();
if (isset($experience_user_id)) {
foreach ($data_profile['experience'] as $key => $value) {
$experience_user = ExperienceUser::find($key);
$experience_user->detail_user_id = $detail_user['id'];
$experience_user->experience = $value;
$experience_user->save();
}
} else {
foreach ($data_profile['experience'] as $key => $value) {
if (isset($value)) {
$experience_user = new ExperienceUser;
$experience_user->detail_user_id = $detail_user['id'];
$experience_user->experience = $value;
$experience_user->save();
}
}
}
toast()->success('Update has been success');
return back();
}
Blade view
<div class="flex items-center mt-1">
#if (auth()->user()->detail_user()->first()->photo)
<img src="{{ asset('storage/assets/photo/'. auth()->user()->detail_user()->first()->photo) }}" alt="photo profile" class="w-16 h-16 rounded-full">
#else
<span class="inline-block w-16 h-16 overflow-hidden bg-gray-100 rounded-full">
<svg class="w-full h-full text-gray-300" fill="currentColor" viewBox="0 0 24 24">
<path d="M24 20.993V24H0v-2.996A14.977 14.977 0 0112.004 15c4.904 0 9.26 2.354 11.996 5.993zM16.002 8.999a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
</span>
#endif
<label for="choose" class="px-3 py-2 ml-5 text-sm font-medium leading-4 text-gray-700 bg-white border border-gray-300 rounded-md shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500">Choose File</label>
<input type="hidden" name="old_photo" value="{{ auth()->user()->detail_user()->first()->photo }}">
<input type="file" accept="image/*" id="choose" name="photo" hidden>
<a href="{{ route('member.delete.photo.profile') }}" type="button" class="px-3 py-2 ml-5 text-sm font-medium leading-4 text-red-700 bg-transparent rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500" onclick="return confirm('Are you sure want to delete your photo?')">
Delete
</a>
</div>
#if ($errors->has('photo'))
<p class="text-red-500 mb-3 text-sm">{{ $errors->first('photo') }}</p>
#endif
</div>
You are not storing image value on database, update this:
if (isset($data_detail_user['photo'])) {
$data_detail_user['photo'] = $request_detail_user->file('photo')->store(
'assets/photo', 'public'
);
$detail_user->photo = $data_detail_user['photo']; //or some uniquely generated image name
}
Check your DetailUser model, is column photo fillable?
try to add this in your Model:
protected $fillable = ['id', 'photo', 'columnName', 'columnName'];
or
protected $guarded = ['id'];
Are you getting an error? If not, you're probably not storing all the values you used in the UpdateDetailUserRequest.
Could you also share the request codes?
UPDATED:
Thank you for sharing the request. Since you are validating photo, role, contact_number and biography. I would recommend to change the following.
Change:
$detail_user->role = $request_detail_user->input('role');
$detail_user->contact_number = $request_detail_user- >input('contact_number');
$detail_user->biography = $request_detail_user->input('biography');
$detail_user->save();
To:
$detail_user->save($request_detail_user->validated());

Stuck on search function for multiple parameters

I have a page with multiple posts, each post has a location and department & title.
I implemented the search function for "word search" and it works fine, but in addition to this I have two Dropdown menus for location & department, they get their content from two different table.
I am kind of stuck and don't know why it doesn't work, here is my controller code:
public function index(Request $request)
{
$posts = Post::orderBy('titel')->get();
$standorts = Standort::orderBy('standort_name')->get();
$abteilungs = Abteilung::orderBy('abteilung_name')->get();
if ($request->filled('s') || $request->has('standort') || $request->has('abteilung')) {
$word = strtolower($request->get('s'));
$location = $request->standort;
$dep = $request->abteilung;
$query = strtolower($request->get('s'));
$posts = $posts->filter(function ($post) use ($dep, $word, $location) {
if (
Str::contains(strtolower($post->Titel), $word) ||
Post::where('standort_name') == $location
|| Post::where('abteilung_name') == $dep
) {
return true;
}
return false;
});
}
return view('posts.overview', [
'posts' => $posts,
'standorts' => $standorts,
'abteilungs' => $abteilungs,
]);
}
Here is my blade code:
<form class="mb-5 flex justify-left grid grid-cols-4 gap-x-20" action=" {{ route('overview') }}"
method="get">
<div class="w-full h-12 text-left col-start-1 col-end-1">
<input placeholder="Schlagwort" type="text" id="s" name="s" value="{{ request()->get('s') }}"
class="mb-2 w-full h-full bg-white bg-opacity-95 rounded focus:ring-2 border border-gray-300 focus:border-indigo-500 text-base outline-none text-gray-700 text-xl py-1 px-3 leading-8">
</div>
<div class="text-left mb-4 h-12">
<select name="abteilung" id="abteilung" class="h-full w-full flex justify-center bg-white bg-opacity-95 rounded focus:ring-2 border border-gray-300 focus:border-indigo-500 text-base outline-none text-gray-700 text-lg text-center leading-8">
<option selected="true" disabled="disabled">Abteilung</option>
#foreach($abteilungs as $abteilung)
<option value="{{ $abteilung->abteilung_name }}">{{ $abteilung->abteilung_name }}</option>
#endforeach
</select>
</div>
<div class="text-left mb-4 h-12">
<select name="standort" id="standort" class="h-full w-full flex justify-center bg-white bg-opacity-95 rounded focus:ring-2 border border-gray-300 focus:border-indigo-500 text-base outline-none text-gray-700 text-lg text-center leading-8">
<option selected="true" disabled="disabled">Standort</option>
#foreach($standorts as $standort)
<option value="{{ $standort->standort_name }}">{{ $standort->standort_name }}</option>
#endforeach
</select>
</div>
<button class="col-start-4 col-end-4 w-11/12 h-12 text-white text-2xl px-4 py-2 rounded text-base font-medium bg-gradient-to-r from-green-400 to-blue-500 float-right shadow transition duration-500 ease-in-out transform hover:-translate-y-1 hover:scale-100">
Suchen
</button>
</form>
When I dd this part:
$word = strtolower($request->get('s'));
$location = $request->standort;
$dep = $request->abteilung;
I get the correct attributes, for instance a word like "HR", a location like "Munich" and a department like "Finance".
When I perform the search though for a specific department all posts are gone.
Edit for clarity:
The goal is that a user can search for a word in a title, a location and department all in one request, and only the post where each is present gets shown.
Edit Relation Models:
Post Model:
class Post extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* #var string[]
*/
protected $fillable = [
'titel',
'startdate',
'enddate',
'beschreibung',
'standort',
'type_name',
'abteilung_name',
'kontakt',
'isActive',
'lebenslauf',
'zeugnisse',
'anschreiben',
'weitere_Doks',
'is_Permitted',
'job_start',
];
public function location() {
return $this->hasOne(Standort::class);
}
public function job_type() {
return $this->hasOne(Jobtypes::class);
}
}
Abteilung Model:
class Abteilung extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* #var string[]
*/
protected $fillable = [
'abteilung_name',
];
public function posts() {
return $this->hasMany(Post::class);
}
}
Standort Model:
class Standort extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* #var string[]
*/
protected $fillable = [
'abteilung_name',
];
public function posts() {
return $this->hasMany(Post::class);
}
}

livewire form not going through laravel

I've been watching a comment livewire tutorial from codecoure. Everything worked fine till a form that will store the comment is not going through. I tried dd, but I get nothing. In the url after the slug it says comment=whateverItyped. i've been using this tutorial in a story posting board, but I'm clueless where the error is. I've even copied and pasted the exact same form and method but nothing goes through the form.
The form:
<div class="min-w-0 flex-1">
<form wire:submit.prevent="postComment">
<div>
<label for="comment" class="sr-only">Comment body</label>
<textarea id="comment" name="comment" rows="3"
class="shadow-sm block w-full focus:ring-blue-500 focus:border-blue-500 border-gray-300 rounded-md #error('newCommentState.body') border-red-500 #enderror"
placeholder="Write something"
wire:model.defer="newCommentState.body">
</textarea>
#error('newCommentState.body')
<p class="mt-2 text-sm text-red-500">{{ $message }}</p>
#enderror
</div>
<div class="mt-3 flex items-center justify-between">
<button type="submit" class="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Comment
</button>
</div>
</form>
</div>
Since I'm clueless where this error is. I don't know what more I should post than the form. There is anohter method that gets the comments, and that works fine. I think it is something with the post request but I'm unsure since I'm new to livewire
namespace App\Http\Livewire;
use Livewire\Component;
class Comments extends Component
{
public $model;
public function postComment()
{
$this->validate([
'newCommentState.body' => 'required'
]);
$comment = $this->model->comments()->make($this->newCommentState);
$comment->user()->associate(auth()->user());
$comment->save();
$this->newCommentState = [
'body' => ''
];
$this->goToPage(1);
}
public function render()
{
$comments = $this->model
->comments()
->with('user', 'children.user', 'children.children')
->parent()
->latest()
->get();
return view('livewire.comments', [
'comments' => $comments
]);
}
}
It seems you are using $newCommentState, but never declares it.
Add a new attribute public $newCommentState;.
I would also create a method like that:
// this will initialize $newCommentState
public function mount() {
$this->newCommentState = [
'body' => ''
];
}

Livewire/Alpine Dom template not updating

first time using Livewire with Alpine and its a real pain to debug. Alpine console errors are so vague, is there anyway to make them more specific and verbose?
I digress.
I'm updating an array on livewire component which, when not empty, should be showing in the DOM. Everything works and when viewed in the console i can see that the changes are being made. inside the console everything is happening as it should. The trouble is, nothing i happening in the browser!
<div x-data="{ ...data() }" class="overflow-hidden wrapper w-full ">
<div class="flex justify-end w-full relative coins-container space-x-6">
<input id="search-toggle" type="search" pclass="block w-full bg-gray-100 focus:outline-none focus:bg-white focus:shadow text-gray-700 font-bold rounded-lg pl-12 pr-4 py-4 shadow-xl" wire:model.debounce.750ms="searched_term" />
</div>
#if($filtered_variable)
<template>
<div class="mt-1 wrapper">
<div id="search-content" class=" w-full text text-gray-600 rounded-lg overflow-y-auto bg-white shadow-xl" style="max-height: 500px;">
<div id="searchresults" class="h-auto w-full mx-auto">
#foreach ($filtered_variable as $index => $value)
<h1 x-text="{{$value['title']}}"></h1>
#endforeach
</div>
</div>
</div>
</template>
#endif
</div>
Class Searchbar extends Component
{
public $first_array;
public $searched_term;
public $filtered_array;
public function mount()
{
$db_content = Stuff::where(function ($query) {
$query->where('thing', false)
->orWhereNull('thing');
})
->with('variable_eg')
->get();
$this->first_array = $db_content;
$this->searched_term = '';
$this->filtered_array = [];
}
public function render()
{
return view('livewire.searchbar');
}
public function updated($name, $value)
{
if (empty($this->searched_term)) {
return '';
}
$this->filtered_array = array_filter($this->first_array, array($this, 'filter'));
public function filter($element)
{
$title = strtolower($element['title']);
if (strpos($title, $this->searched_term) !== false) {
return true;
}
}
}
inside the console I can see that my alpine/livewire component is receiving the filtered_value just as expected. But nothing is happening on the browser. How can I force rerender?
To update (refresh/rerender) your component you can use a listener
protected $listeners = ['refreshComponent' => '$refresh'];
Then when the refreshComponent event is emitted, it will refresh the component without running any other actions.
There is a Github discussion in the Livewire repo describing this.
Regarding your updating and filter methods (just as a hint):
There are also special methods like updatingFoo, updatedFoo that run before/after an item foo is updating. See the Lifecycle Hooks.

Getting error "Typed property must not be accessed before initialization" - Laravel Livewire

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();
}

Categories