Livewire add comment modal component - laravel

I'm struggling to get a form parameter submitted to a livewire modal. I'm using laravel9 with php8.1 and have installed wire-elements/modal via composer (v1.0.6) I followed this guide.
So I have a modal.blade.php file in my view components folder with the following:
#props(['formAction' => false])
<div>
#if($formAction)
<form wire:submit.prevent="{{ $formAction }}">
#endif
<div class="bg-white p-4 sm:px-6 sm:py-4 border-b border-gray-150">
#if(isset($title))
<h3 class="text-lg leading-6 font-medium text-gray-900">
{{ $title }}
</h3>
#endif
</div>
<div class="bg-white px-4 sm:p-6">
<div class="space-y-6">
{{ $content }}
</div>
</div>
<div class="bg-white px-4 pb-5 sm:px-4 sm:flex">
{{ $buttons }}
</div>
#if($formAction)
</form>
#endif
</div>
On my test page I have a link to open the Modal which contains a text area for the comment:
<x-jet-button wire:click="$emit( 'openModal', 'add-comment',{{ json_encode(['job_number' => $job->number]) }} )">
{{ __('Add') }}
</x-jet-button>
The livewire component is as follows:
(add-comment.blade.php)
<x-modal formAction="create">
<x-slot name="title">
Add a Comment
</x-slot>
<x-slot name="content">
<label for="comment">Comment Narrative:</label>
<textarea id="comment" name="comment" rows="4" cols="36"></textarea>
</x-slot>
<x-slot name="buttons">
<x-jet-button class="mx-2" type="submit">
{{ __('Submit') }}
</x-jet-button>
<x-jet-button type="button" class="mx-2" wire:click="$emit('closeModal')">
{{ __('Close') }}
</x-jet-button>
</x-slot>
</x-modal>
(AddComment.php)
<?php
namespace App\Http\Livewire;
use App\Models\JobComment;
use Illuminate\Support\Facades\Auth;
use LivewireUI\Modal\ModalComponent;
class AddComment extends ModalComponent
{
public string $comment = '';
public int $job_number;
protected $listeners =['refresh' => '$refresh'];
public function render()
{
return view('livewire.add-comment');
}
public function create()
{
$comment = new JobComment();
$comment->comment = $this->comment;
$comment->job_number = $this->job_number;
$comment->user_id = Auth::id();
$comment->save();
$this->closeModal();
}
public static function closeModalOnEscape(): bool
{
return false;
}
public static function destroyOnClose(): bool
{
return true;
}
}
When i click the button on my test page the modal appears and has the desired content. If I enter some text in the textarea and click the submit a new comment is added to the database but the comment itself is an empty string.
In addition if I click the close button on this form I am unable to reopen the modal but if I click outside the modal it closes and can be opened again (but saving this comment is the issue here).
I have tried using true instead of false in the modal.blade.php file (#props(['formAction' => true])) but it made no difference and my comment is empty
Any ideas?

So with this the issue was i hadn't binded to the model so I amended the text area element to this
<textarea wire:model.defer="comment" id="comment" rows="4" cols="36">

Related

Attempt to read property "menus" on null

when i run laravel project on my computer after doing php artisan serve as soon as it login it shows error "Attempt to read property "menus" on null"
Please help me
#foreach ($specials->menus as $menu)
<div class="max-w-xs mx-4 mb-2 rounded-lg shadow-lg">
<img class="w-full h-48" src="{{ Storage::url($menu->image) }}" alt="Image" />
<div class="px-6 py-4">
<h4 class="mb-3 text-xl font-semibold tracking-tight text-green-600 uppercase">
{{ $menu->name }}</h4>
<p class="leading-normal text-gray-700">{{ $menu->description }}.</p>
</div>
<div class="flex items-center justify-between p-4">
<span class="text-xl text-green-600">${{ $menu->price }}</span>
</div>
</div>
#endforeach
welcomecontroller.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Menu extends Model
{
use HasFactory;
protected $fillable = ['name', 'price', 'description', 'image'];
public function categories()
{
return $this->belongsToMany(Category::class, 'category_menu');
}
}
I suppose you forgot to initialize $specials, it is null, that's why you get the error.
Could you try to use dd before foreach statement, so we can see it's content?
{{ dd($specials) }}
UPDATE:
#if(!is_null($specials))
#foreach ($specials->menus as $menu)
<div class="max-w-xs mx-4 mb-2 rounded-lg shadow-lg">
<img class="w-full h-48" src="{{ Storage::url($menu->image) }}" alt="Image" />
<div class="px-6 py-4">
<h4 class="mb-3 text-xl font-semibold tracking-tight text-green-600 uppercase">
{{ $menu->name }}</h4>
<p class="leading-normal text-gray-700">{{ $menu->description }}.</p>
</div>
<div class="flex items-center justify-between p-4">
<span class="text-xl text-green-600">${{ $menu->price }}</span>
</div>
</div>
#endforeach
#else
<div>no item was found</div>
#endif
Your issue is with this line of code (from the comments):
$specials = Category::where('name', 'specials')->first();
->first(); can return null, so when you use it later as $specials->menus, it can be null->menus, which is not valid.
You can use ->firstOrFail() to trigger a 404 in the event $specials results in null, or #foreach($specials->menus ?? [] as $menu) to short-circuit your foreach() with an empty array if $specials is null:
In your Controller:
$specials = Category::where('name', 'specials')->firstOrFail();
return view('welcome', compact('specials'));
OR
In your View:
#foreach($specials->menus ?? [] as $menu)
...
#endforeach
Either case will properly handle your "unsafe" code (unsafe meaning functional, but possible for unhandled errors, like null->menus, etc.) and either trigger a 404 before the view is rendered, or perform a foreach() on an empty array, which does nothing.

Laravel Livewire component not re-rendering

I have a notifications component that I'm trying to emit an event to and get it to refresh and show new notifications to the user. I've verified that I'm receiving the event, and the refreshNotifications method is being called, but no matter what I try I can't seem to get the actual component to update. It's getting super frustrating at this point.
Here's my component:
namespace App\Http\Livewire\Components;
use Livewire\Component;
use Illuminate\Support\Facades\Auth;
class Notifications extends Component
{
public $notifications;
public $notificationCount = 0;
protected $listeners = ['refreshNotifications'];
public function mount()
{
$this->user = Auth::user();
}
public function render()
{
$this->notifications = $this->user->fresh()->unreadNotifications;
$this->notificationCount = $this->user->fresh()->unreadNotifications->count();
return view('livewire.components.notifications');
}
public function clearNotifications()
{
$this->user->unreadNotifications()->update(['read_at' => now()]);
}
public function refreshNotifications()
{
//can verify here that the event successfully emitted to this component. No refresh happens though.
$this->render();
}
}
And here's my blade view:
<li class="nav-item dropdown" x-data="{ open: false }">
<a #click="open = !open" #click.outside="open = false" class="nav-link dropdown-toggle show" id="notificationDropdown" role="button">
<i class="mdi mdi-bell-outline h3"></i>
#if ($notificationCount)
<div class="indicator">
<div class="circle"></div>
</div>
#endif
</a>
<div x-show="open" class="dropdown-menu p-0 show" data-bs-popper="none">
<div class="px-3 py-2 d-flex align-items-center justify-content-between border-bottom">
<p>{{ $notificationCount }} New Notifications</p>
#if ($notificationCount)
<a role="button" wire:click="clearNotifications" class="text-muted"><i class="mdi mdi-notification-clear-all"></i>Clear all</a>
#endif
</div>
#foreach ($notifications as $notification)
<div class="px-3 py-2" wire:key="notification-{{ $notification->id }}">
<a role="button" class="d-flex align-items-center py-2">
<div class="flex-grow-1 me-2">
<h5>{{ $notification->data['title'] ?? 'Test Title' }}</h5>
<p class="tx-14 text-muted"><em>{{ $notification->data['body'] ?? 'Test body' }}</em></p>
<p class="tx-12 text-muted">{{ Helper::timeElapsedForHumans($notification->created_at) }}</p>
</div>
</a>
</div>
#endforeach
</div>
</li>
Not sure if it's helpful, but the xhr response is this: {"effects":{"html":null,"dirty":[]},"serverMemo":{"checksum":"c980388fe95899e9055bfc363bd57c5d80916fe581b5b7983109cdba20db2f33"}}
Other questions on SO are related to not being in a single root element. That's not my issue. I have no idea why this isn't working. I feel like I've tried everything.
You need to define what event is called in the listener, for example:
protected $listeners = ['refreshNotifications' => '$refresh'];
See here:
https://laravel-livewire.com/docs/2.x/actions#magic-actions

Trix editor How comes that the toolbar disappear on editor blur?

I try to use trix editor in Laravel8 livewire. I could manage to have it working but there is one point that is very annoying.
I use a trix livewire component that is inserted in a Post component.
I is suggested to use the trix-blur event to save the content of the editor. This works perfectly and I can use a store button to have the value of the trix editor component saved in the post record in the database.
The annoying point is that when the trix-blur event occurs, the tool bar disappears and never shows up again even if the editor regains the focus. Thus the user cannot use it any longer.
What can I do to prevent this behavior.
here is the trix component
<?php
namespace App\Http\Livewire;
use Livewire\Component;
class Trix extends Component
{
public $value;
public $trixId;
const EVENT_VALUE_UPDATED = 'trix_value_updated';
public function mount($value = ''){
$this->value = $value;
$this->trixId = 'trix-' . uniqid();
}
public function render()
{
return view('livewire.trix');
}
public function updatedValue($value){
$this->emit(self::EVENT_VALUE_UPDATED, $this->value);
}
}
and its view
<div>
<input id="{{ $trixId }}" type="hidden" name="content" value="{{ $value }}">
<trix-editor wire:ignore class="text-white" input="{{ $trixId }}"></trix-editor>
</div>
<script>
var trixEditor = document.getElementById("{{ $trixId }}")
addEventListener("trix-blur", function(event) {
#this.set('value', trixEditor.getAttribute('value'))
})
</script>
and here is related part of the post view
<div>
<div class="button-line flex flex-row">
<span class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto">
<button wire:click.prevent="store()" id="submit" type="button" class="button-register">
Enregistrer l'info-lettre
</button>
</span>
<span class="mt-3 flex w-full rounded-md shadow-sm sm:mt-0 sm:w-auto">
<button wire:click="closeModalPopover()" type="button" class="button-cancel">
Abandonner
</button>
</span>
</div>
#livewire('trix',['value'=>$body])
</div>
I finally found an answer here
https://devdojo.com/tnylea/laravel-livewire-trix-editor-component
The answer is at the end in the Bonus part. Note only the trix editor input must have a wire:ignore but also the div that surrounds it
<div wire:ignore>
<input id="{{ $trixId }}" type="hidden" name="content" value="{{ $value }}">
<trix-editor wire:ignore class="text-white" input="{{ $trixId }}"></trix-editor>
</div>

LaravelShoppingcart package, rowId property not working

Hi I'm using the LaravelShoppingcart package(https://github.com/bumbummen99/LaravelShoppingcart) and When I output {{ $item->rowId }} on the blade template I get the rowId on the browser when the blade is rendered but when I pass it in the qtyIncreased({{ $item->rowId}}) method and try to dump and die it in the livewire component it doesn't work. Mark you when I pass the $item->id and dump and die it, it works. The only error I'm getting is on the console and is:
Error handling response: TypeError: Cannot destructure property 'yt' of 'undefined' as it is undefined.
at chrome-extension://cmedhionkhpnakcndndgjdbohmhepckk/scripts/contentscript.js:535:6
Any assistance is highly appreciated.
Live wire blade component
<div class="cart-overlay {{ $active ? 'transparent' : '' }}">
<div class="cart {{ $active ? 'showCart' : '' }}">
<span class="close-cart" wire:click="$emit('closeCart')">
<i class="fas fa-window-close"></i>
</span>
<h2>Cart</h2>
<div class="cart-content">
#foreach ($cartContent as $item)
<div class="cart-item">
<img src="{{ $item->options->image }}" alt="product" />
<div>
<h4>{{ $item->name }}</h4>
<h5>${{ $item->price }}.99</h5>
<span class="remove-item">remove</span>
</div>
<div>
<i class="fas fa-chevron-up" wire:click="qtyIncreased({{ $item-
>rowId }})"></i>
<p class="item-amount">{{ $item->qty }}</p>
<i class="fas fa-chevron-down"></i>
</div>
</div>
#endforeach
</div>
<div class="cart-footer">
<h3>your total : $ <span class="cart-total">{{ $cartTotal }}</span></h3>
<button class="clear-cart banner-btn">clear cart</button>
</div>
</div>
</div>
Livewire Class component
<?php
namespace App\Http\Livewire;
use Gloudemans\Shoppingcart\Facades\Cart;
use Livewire\Component;
class CartOverlay extends Component
{
public $active = false;
public function render()
{
$cartContent = Cart::content();
$cartTotal = Cart::total();
return view('livewire.cart-overlay', compact(['cartContent', 'cartTotal']));
}
public $listeners = [
'showCart',
'closeCart'
];
public function qtyIncreased($rowId)
{
dd($rowId);
}
public function showCart()
{
$this->active = true;
}
public function closeCart()
{
$this->active = false;
}
}
I managed to fixed it, the parameter in the function should be passed in as a string. So, I added quotes around the parameter. Changed it from <i class="fas fa-chevron-up" wire:click="qtyIncreased({{ $item->rowId }})"></i>
TO
<i class="fas fa-chevron-up" wire:click="qtyIncreased('{{ $item->rowId }}')"></i>

WithPagination with bootstrap 4 as paginationTheme not working as intended in livewire

I have made a simple project in Laravel. I am using Livewire v^2.2.
When I add the code as described in the documentation, the previous and next buttons are dead.
The code in php Component is:
// If I comment this out, the view is horrible but is working.
// use WithPagination;
// protected $paginationTheme = 'bootstrap';
public function render()
{
return view('livewire.users', [
'users' => User::paginate(1),
]);
}
In the template:
<div>
<h1>Users</h1>
<div class="list-group">
#foreach($users as $user)
<a href="" class="list-group-item list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">{{ $user->name }}</h5>
<small>{{ $user->created_at }}</small>
</div>
<p class="mb-1">{{ $user->email }}</p>
</a>
#endforeach
</div>
<br>
{{ $users->links() }}
</div>
try this
use Livewire\WithPagination;
class NameComponent extends Component
{
use WithPagination;
protected $paginationTheme = 'bootstrap';
add this in side Livewire view
<div>
.....
#push('scripts')
<script>
Livewire.restart();
</script>
#endpush
</div>
in layouts/app.blade.php after #livewireScripts add #stack('scripts')
<body>
...
#livewireScripts
#stack('scripts')
</body>
I found the reason why.
Livewire components MUST have a single root element.
I forgot to add the top line in the question i see.

Resources