Force render to bypass partial rendering (#17862)

* Skip partial rendering with forceRender

* Add department schema

* Test force render with create another in relation manager

* chore: fix code style

* Test force render with create another in relation manager

* Move forceRender into InteractsWithActions

* chore: fix code style

* Early return dehydrate if force render

* Add helper method to force render after create another

* Update CreateAction.php

---------

Co-authored-by: Dan Harrin <git@danharrin.com>
This commit is contained in:
Ben
2025-09-20 12:59:27 +01:00
committed by GitHub
co-authored by Dan Harrin
parent 9272e5ad4f
commit def8851970
7 changed files with 85 additions and 1 deletions
@@ -332,6 +332,11 @@ trait InteractsWithActions
return $result;
}
public function forceRender(): void
{
app(PartialsComponentHook::class)->forceRender($this);
}
protected function partiallyRenderActionParentSchema(Action $action): void
{
$actionSchema = $action->getSchemaContainer() ?? $action->getSchemaComponent()?->getContainer();
+22
View File
@@ -23,6 +23,8 @@ class CreateAction extends Action
protected bool | Closure $canCreateAnother = true;
protected bool | Closure $shouldForceRenderAfterCreateAnother = false;
protected ?Closure $modifyCreateAnotherActionUsing = null;
protected ?Closure $preserveFormDataWhenCreatingAnotherUsing = null;
@@ -107,6 +109,14 @@ class CreateAction extends Action
$schema->model($record)->saveRelationships();
if ($arguments['another'] ?? false) {
if ($this->shouldForceRenderAfterCreateAnother()) {
$livewire = $this->getLivewire();
if (method_exists($livewire, 'forceRender')) {
$livewire->forceRender();
}
}
$this->callAfter();
$this->sendSuccessNotification();
@@ -157,6 +167,13 @@ class CreateAction extends Action
return $this;
}
public function forceRenderAfterCreateAnother(bool | Closure $condition = true): static
{
$this->shouldForceRenderAfterCreateAnother = $condition;
return $this;
}
/**
* @deprecated Use `createAnother()` instead.
*/
@@ -192,6 +209,11 @@ class CreateAction extends Action
return true;
}
public function shouldForceRenderAfterCreateAnother(): bool
{
return (bool) $this->evaluate($this->shouldForceRenderAfterCreateAnother);
}
public function getRelationship(): Relation | Builder | null
{
return $this->evaluate($this->getRelationshipUsing) ?? $this->getTable()?->getRelationship() ?? $this->getHasActionsLivewire()?->getDefaultActionRelationship($this);
@@ -17,6 +17,10 @@ class PartialsComponentHook extends ComponentHook
{
public function shouldSkipRender(): bool
{
if ($this->shouldForceRender()) {
return false;
}
if (! $this->isLackingPartialRendersToCoverAllCallsAndUpdates()) {
return true;
}
@@ -62,6 +66,11 @@ class PartialsComponentHook extends ComponentHook
return ($updatesCount + $callsCount) !== intval($this->storeGet('partialRendersCount') ?? 0);
}
public function shouldForceRender(): bool
{
return store($this->component)->get('forceRender', false);
}
public function shouldRenderMountedActionOnly(): bool
{
if (! property_exists($this->component, 'mountedActions')) {
@@ -100,6 +109,10 @@ class PartialsComponentHook extends ComponentHook
public function dehydrate(ComponentContext $context): void
{
if ($this->shouldForceRender()) {
return;
}
$partials = [];
$renderAndQueuePartials = function (Closure $getPartialsUsing) use (&$partials): void {
@@ -173,6 +186,11 @@ class PartialsComponentHook extends ComponentHook
$this->recordPartialRender($component);
}
public function forceRender(Component $component, bool $forceRender = true): void
{
store($component)->set('forceRender', $forceRender);
}
public function renderPartial(Component $component, Closure $renderUsing): void
{
store($component)->push('partials', $renderUsing);
+2
View File
@@ -13,6 +13,8 @@ class Department extends Model
use HasFactory;
use SoftDeletes;
protected $guarded = [];
protected static function newFactory()
{
return DepartmentFactory::new();
@@ -2,6 +2,7 @@
namespace Filament\Tests\Fixtures\Resources\Departments\Schemas;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Schema;
class DepartmentForm
@@ -10,7 +11,7 @@ class DepartmentForm
{
return $schema
->components([
//
TextInput::make('name')->required(),
]);
}
}
@@ -4,7 +4,9 @@ namespace Filament\Tests\Fixtures\Resources\Tickets\RelationManagers;
use Filament\Actions\CreateAction;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Schemas\Schema;
use Filament\Tables\Table;
use Filament\Tests\Fixtures\Resources\Departments\Schemas\DepartmentForm;
use Filament\Tests\Fixtures\Resources\Departments\Tables\DepartmentsTable;
class DepartmentsRelationManager extends RelationManager
@@ -18,4 +20,9 @@ class DepartmentsRelationManager extends RelationManager
CreateAction::make(),
]);
}
public function form(Schema $schema): Schema
{
return DepartmentForm::configure($schema);
}
}
@@ -11,6 +11,7 @@ use Filament\Actions\RestoreAction;
use Filament\Actions\RestoreBulkAction;
use Filament\Actions\Testing\TestAction;
use Filament\Actions\ViewAction;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tests\Fixtures\Models\Department;
use Filament\Tests\Fixtures\Models\Ticket;
use Filament\Tests\Fixtures\Policies\DepartmentPolicy;
@@ -18,8 +19,10 @@ use Filament\Tests\Fixtures\Resources\Tickets\Pages\EditTicket;
use Filament\Tests\Fixtures\Resources\Tickets\RelationManagers\DepartmentsRelationManager;
use Filament\Tests\Panels\Resources\TestCase;
use Illuminate\Auth\Access\Response;
use Illuminate\Support\Str;
use function Filament\Tests\livewire;
use function Pest\Laravel\assertDatabaseHas;
uses(TestCase::class);
@@ -150,3 +153,29 @@ it('renders actions based on policy', function (string $action, string $policyMe
'restore bulk action with policy returning allowed response' => fn (): array => [RestoreBulkAction::class, 'restoreAny', Response::allow(), true, true, true],
'restore bulk action with policy returning false' => fn (): array => [RestoreBulkAction::class, 'restoreAny', false, false, true, true],
'restore bulk action with policy returning denied response' => fn (): array => [RestoreBulkAction::class, 'restoreAny', Response::deny(), false, true, true]]);
it('can force render relation manager after create another', function (): void {
$ticket = Ticket::factory()
->create();
CreateAction::configureUsing(function (CreateAction $action): void {
$action->forceRenderAfterCreateAnother(fn (mixed $livewire): bool => $livewire instanceof RelationManager);
});
$action = TestAction::make(CreateAction::class)->table();
livewire(DepartmentsRelationManager::class, ['ownerRecord' => $ticket, 'pageClass' => EditTicket::class])
->assertSuccessful()
->assertCountTableRecords(0)
->assertActionExists($action)
->mountAction($action, ['another' => true])
->fillForm([
'name' => $name = Str::random(),
])
->callMountedAction()
->assertHasNoFormErrors()
->assertCountTableRecords(1)
->assertSeeText($name);
assertDatabaseHas(Department::class, ['name' => $name]);
});