How to re-render/refresh the parent form after a new entry of a RelationManager has been created?

Hello, I want to refresh the main form/resource (or the whole page if possible) after a new entry has been created through a RelationManager of the resource. This is my setup: - Resource: PriceListResource - RelationManager: SectionsRelationManager - Relationship name: sections I have tried this so far: 1. RelationsManager/SectionsRelationManager.php:
// ...
->headerActions([
Tables\Actions\CreateAction::make()
->after(fn () => $this->dispatch('section-added')),
])
// ...
// ...
->headerActions([
Tables\Actions\CreateAction::make()
->after(fn () => $this->dispatch('section-added')),
])
// ...
Then in the edit resource page I listen to that event: 2. PriceListResource/Pages/EditPriceList.php
use Livewire\Attributes\On;

// ...

#[On('section-added')]
public function refreshForm(): void
{
$this->refreshFormData(['sections']);
}
use Livewire\Attributes\On;

// ...

#[On('section-added')]
public function refreshForm(): void
{
$this->refreshFormData(['sections']);
}
The event/listener works (I added a dd() in there and I can see it being triggered) but I can make it refresh the relationship. I guess because sections is not an attribute per-se. The reason why I need this is because within the form I have a TableRepeater that is built dynamically based on this relation. Then, how could I achieve the refresh/re-render?
Solution:
@bardolf_6969 What I ended up doing was a full refresh. Let me share you the details. 1. Event dispatch from RelationsManager/SectionsRelationManager.php: ```php // ......
Jump to solution
13 Replies
bardolf_6969
bardolf_69694mo ago
struggling with the same issue, any pointers?
Solution
kennyhorna
kennyhorna4mo ago
@bardolf_6969 What I ended up doing was a full refresh. Let me share you the details. 1. Event dispatch from RelationsManager/SectionsRelationManager.php:
// ...
->headerActions([
Tables\Actions\CreateAction::make()
->after(fn () => $this->dispatch('sections-modified')),
])
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\DeleteAction::make()
->after(fn () => $this->dispatch('sections-modified')),
])
// ...
// ...
->headerActions([
Tables\Actions\CreateAction::make()
->after(fn () => $this->dispatch('sections-modified')),
])
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\DeleteAction::make()
->after(fn () => $this->dispatch('sections-modified')),
])
// ...
2. Set up listener on list page PriceListResource/Pages/EditPriceList.php
use Livewire\Attributes\On;

// ...

#[On('section-modified')]
public function refreshForm(): void
{
/** @var PriceList $record */
$record = $this->record;
$this->redirect(self::getUrl([
'record' => $record->id,
'tab' => 'sections',
]));
}
use Livewire\Attributes\On;

// ...

#[On('section-modified')]
public function refreshForm(): void
{
/** @var PriceList $record */
$record = $this->record;
$this->redirect(self::getUrl([
'record' => $record->id,
'tab' => 'sections',
]));
}
So, it pretty much does a redirect to the same page. Ps: The additional parameter that I pass in there is the tab. This is a query param to set the active tab of the tabs component. Since I'm interested in the tab called "sections", I will check if there is the query param in the url to set it dynamically, because by default it will open the first tab only, even if you refresh the page with another one active. If you want to know how this works, then you can do this (in the resource): PriceListResource:
public static function form(Form $form): Form
{
return $form
->schema([
Tabs::make('tabs')
->schema([/** ... */])
->activeTab(fn () => request('tab') === 'sections' ? 2 : 1), // <-- my tab nº 2 is the "sections" one.
]);
}
public static function form(Form $form): Form
{
return $form
->schema([
Tabs::make('tabs')
->schema([/** ... */])
->activeTab(fn () => request('tab') === 'sections' ? 2 : 1), // <-- my tab nº 2 is the "sections" one.
]);
}
I hope this helps you.
slakkie
slakkie3mo ago
Hey @kennyhorna seems like you've played with dispatching and listening to events. I'm going crazy on this one. Here my scenario using filament 3 : I have a ProductResource and next to each product in a table I have a add to cart button/action:
->actions([
Action::make('basket')
->requiresConfirmation()
->after(function () {
$this->dispatch('updated_cart'); <-- throws error: Using $this when not in object context
})
->icon('heroicon-o-shopping-cart')
->iconButton()
->action(function (Event $record) {
app()->make('cart')->addEvent($record->id);
}),
])
->actions([
Action::make('basket')
->requiresConfirmation()
->after(function () {
$this->dispatch('updated_cart'); <-- throws error: Using $this when not in object context
})
->icon('heroicon-o-shopping-cart')
->iconButton()
->action(function (Event $record) {
app()->make('cart')->addEvent($record->id);
}),
])
First when I try to dispatch the event like in the above code I get an error: Using $this when not in object context I've tried various variants for example, the one below does not throw an error but I am also not sure if the event is even dispatched at all:
->after(function (StaticAction $action) {
$action->dispatch('updated_cart');
})
->after(function (StaticAction $action) {
$action->dispatch('updated_cart');
})
There's a simple filament page with a side navigation and a badge counter. I've tried various ways to listen for the event without getting it to refresh. When a user add an item to the cart it should update via listening for the 'updated_cart' event:
class Cart extends Page
{
protected static ?string $navigationIcon = 'heroicon-o-shopping-cart';

protected static string $view = 'filament.pages.test';

protected $listeners = ['updated_cart' => '$refresh'];

#[On('updated_cart')]
public static function refreshBadgeCount()
{
$count = app()->make('cart')->eventCount();

return $count > 0 ? $count : null;
}

#[On('updated_cart')]
public static function getNavigationBadge(): ?string
{
return static::refreshBadgeCount();
}
class Cart extends Page
{
protected static ?string $navigationIcon = 'heroicon-o-shopping-cart';

protected static string $view = 'filament.pages.test';

protected $listeners = ['updated_cart' => '$refresh'];

#[On('updated_cart')]
public static function refreshBadgeCount()
{
$count = app()->make('cart')->eventCount();

return $count > 0 ? $count : null;
}

#[On('updated_cart')]
public static function getNavigationBadge(): ?string
{
return static::refreshBadgeCount();
}
Do you have maybe any tips or tricks for this?
kennyhorna
kennyhornaOP3mo ago
Is it a typo or the reason why the event is not listened given that you are dispatching 'updated_cart' -> but listening to 'cart-updated'? (check the #[On('...')])
slakkie
slakkie3mo ago
I'm quickly double-checking I've fixed that typo, still not working, I think the issue is how to dispatch the event. As mentioned in my code above when I do: $this->dispatch() it gives me the error.
kennyhorna
kennyhornaOP3mo ago
Try adding a dd('reached!); inside the method of the listener (refreshBadgeCount()). Just to double check if it's going inside. Another thing that I noticed is that you have two methods listening to the event. This is not wrong though but I think that the getNavigationBadge() should be called after the other one, right? If so, then I think you should call it afterwards or inside it. But that's just a comment
slakkie
slakkie3mo ago
Yeah in the actual code there is only 1, it was just to illustrate where I have tried it. Quickly trying the dd() Ok tried the dd()
class Cart extends Page
{
protected static ?string $navigationIcon = 'heroicon-o-shopping-cart';

protected static string $view = 'filament.pages.test';

protected $listeners = ['updated_cart' => '$refresh'];

#[On('updated_cart')]
public static function test()
{
dd('x');
}

public static function refreshBadgeCount()
{
$count = app()->make('cart')->eventCount();

return $count > 0 ? $count : null;
}

public static function getNavigationBadge(): ?string
{
return static::refreshBadgeCount();
}
class Cart extends Page
{
protected static ?string $navigationIcon = 'heroicon-o-shopping-cart';

protected static string $view = 'filament.pages.test';

protected $listeners = ['updated_cart' => '$refresh'];

#[On('updated_cart')]
public static function test()
{
dd('x');
}

public static function refreshBadgeCount()
{
$count = app()->make('cart')->eventCount();

return $count > 0 ? $count : null;
}

public static function getNavigationBadge(): ?string
{
return static::refreshBadgeCount();
}
Not reached. Hence I think it is on the dispatching side where the problem lies.
kennyhorna
kennyhornaOP3mo ago
@slakkie Is that filament page that you mention loaded when you trigger the event? I mean, is that view rendered on that screen?
kennyhorna
kennyhornaOP3mo ago
Because if that is in another side and you expect this to update another page (in another tab for example) then you will need to broadcast notifications instead https://filamentphp.com/docs/3.x/notifications/broadcast-notifications
slakkie
slakkie3mo ago
So the product list page is a resource with a table which also have the button to add something to the cart. The Cart Page is a separate component, when viewing the products the cart only have a navigation item with a badge with the number of items in the cart. so in affect they are two seperate components
slakkie
slakkie3mo ago
Thanks for the feedback and help! Appreciate it a lot, been struggling with this for many hours. Not sure how the Notifications can be used to get a Livewire component to refresh? Maybe I should just restate that the ProductResource is a standard Filament resource component with a table and the add to cart button next to each row. When the user click the button the item is added via a service to a cart. The Cart component is also separate component (Page component) not related to the ProductResource at all, with just a page and a navigation item with a badge with the count of items in the cart. So what I want to achieve is that when a product is added to the cart that the left navigation counter badge updates. I thought the easiest would be to dispatch an event and have the Cart component refresh. But as mentioned the listener is not reached. I was wondering if the standard navigation item itself is refreshable, or if the navigation pane can be refresh upon an event or event hook?
No description
No description
kennyhorna
kennyhornaOP3mo ago
Sorry, I went into meetings and I forgot about this. But I found this https://github.com/filamentphp/filament/discussions/7221#discussioncomment-6551399
GitHub
refresh navigation badge · filamentphp filament · Discussion #7221
Table polling works fine, but how can I update the corresponding navigation badge so that they are in sync?
slakkie
slakkie3mo ago
Hey no worries, thanks for all your help! It's really quite a draw-back that there is not comms between some components. It goes against logic 🙂 I've settled on refreshing the page:
->action(function (Event $record, StaticAction $action, $livewire) {
app()->make('cart')->addEvent($record->id);
$action->redirect(request()->header('Referer'));
->action(function (Event $record, StaticAction $action, $livewire) {
app()->make('cart')->addEvent($record->id);
$action->redirect(request()->header('Referer'));
Again thanks for you help!!
Want results from more Discord servers?
Add your server