Issue in using custom component within Repeater

* I created a custom page to manage Course content which extends Filament\Resources\Pages\EditRecord. * Also, I created a custom component called ImportLessonMaterial which extends Filament\Forms\Components\Component * in it's view filament/import-lesson-material.blade.php I call a liveware component called ImportLessonMaterial which extends Livewire\Component The component itself works fine, but when i try to add new unit, or new lesson in the main page I face an issue that says Uncaught Snapshot missing on Livewire component with id: 7VKNgVgSmdefeGmnuDl9 It goes if I remove the custom component from the schema, I'll attach all the related code in this thread. Thanks!!
13 Replies
Mohammed Ouda
Mohammed OudaOP4mo ago
The main edit page code:
use App\Forms\Components\ImportLessonMaterial;
...other imports

class ManageCourseContent extends EditRecord
{
...attributes and methods

public function form(Form $form): Form
{
return $form
->schema([
...other components
Repeater::make('units')
->relationship()
->reorderable()
->orderColumn('ordering')
->schema([
...other components
Repeater::make('lessons')
->label('Unit Lessons')
->relationship()
->reorderable()
->orderColumn('ordering')
->schema([
Grid::make()
->schema([
...other components
Repeater::make('lessonMaterials')
->relationship()
->reorderableWithButtons()
->orderColumn('ordering')
->schema([...other components])
->columnSpanFull()
->default([])
->deleteAction(fn (Action $action) => $action->requiresConfirmation())
->addable(false),
ImportLessonMaterial::make('import_lesson_material')
->columnSpan('full')
]),
])
])
]);
}

...other methods
}
use App\Forms\Components\ImportLessonMaterial;
...other imports

class ManageCourseContent extends EditRecord
{
...attributes and methods

public function form(Form $form): Form
{
return $form
->schema([
...other components
Repeater::make('units')
->relationship()
->reorderable()
->orderColumn('ordering')
->schema([
...other components
Repeater::make('lessons')
->label('Unit Lessons')
->relationship()
->reorderable()
->orderColumn('ordering')
->schema([
Grid::make()
->schema([
...other components
Repeater::make('lessonMaterials')
->relationship()
->reorderableWithButtons()
->orderColumn('ordering')
->schema([...other components])
->columnSpanFull()
->default([])
->deleteAction(fn (Action $action) => $action->requiresConfirmation())
->addable(false),
ImportLessonMaterial::make('import_lesson_material')
->columnSpan('full')
]),
])
])
]);
}

...other methods
}
ImportLessonMaterial Component code:
<?php

namespace App\Forms\Components;

use Filament\Forms\Components\Component;

class ImportLessonMaterial extends Component
{
protected string $view = 'filament.import-lesson-material';

public static function make(string $name): static
{
$static = app(static::class, ['name' => $name]);
$static->configure();

return $static;
}
}
<?php

namespace App\Forms\Components;

use Filament\Forms\Components\Component;

class ImportLessonMaterial extends Component
{
protected string $view = 'filament.import-lesson-material';

public static function make(string $name): static
{
$static = app(static::class, ['name' => $name]);
$static->configure();

return $static;
}
}
the filament.import-lesson-material blade:
@php
$course = $this->getRecord();
$lesson = $getState();
@endphp

@if($course && isset($lesson['id']))
@livewire('import-lesson-material', ['course' => $course, 'lesson' => $lesson])
@endif
@php
$course = $this->getRecord();
$lesson = $getState();
@endphp

@if($course && isset($lesson['id']))
@livewire('import-lesson-material', ['course' => $course, 'lesson' => $lesson])
@endif
the liveware import-lesson-material component code:
<?php

namespace App\Livewire;

use Livewire\Component;
use Filament\Forms\Contracts\HasForms;
use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Forms\Form;
use Illuminate\Contracts\View\View;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Grid;
use Awcodes\TableRepeater\Components\TableRepeater;
use Awcodes\TableRepeater\Header;
use Filament\Forms\Components\Checkbox;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\View as ViewComponent;
use Filament\Notifications\Notification;
use App\Forms\Components\PaginatedSelect;
use Filament\Forms\Set;
use Filament\Forms\Get;

class ImportLessonMaterial extends Component implements HasForms
{
use InteractsWithForms;

public $course;

public $lesson;

public ?array $data = [
'org_library_access_authorization_id' => null,
'general_topic_id' => null,
'special_topic_id' => null,
'learning_object_id' => null,
'pagination' => [
'general_topic_id' => ['current_page' => 0, 'last_page' => 0, 'total' => 0, 'data' => [], 'search' => null],
'special_topic_id' => ['current_page' => 0, 'last_page' => 0, 'total' => 0, 'data' => [], 'search' => null],
'learning_object_id' => ['current_page' => 0, 'last_page' => 0, 'total' => 0, 'data' => [], 'search' => null]
],
'materials' => []
];

public function mount($course, $lesson): void
{
$this->course = $course;
$this->lesson = $lesson;
$this->form->fill($this->data);
}

public function form(Form $form): Form
{
return $form
->schema([...form components])
->statePath('data');
}

public function create(): void
{
...create method logic
}

public function render(): View
{
return view('livewire.import-lesson-material');
}
}
<?php

namespace App\Livewire;

use Livewire\Component;
use Filament\Forms\Contracts\HasForms;
use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Forms\Form;
use Illuminate\Contracts\View\View;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Grid;
use Awcodes\TableRepeater\Components\TableRepeater;
use Awcodes\TableRepeater\Header;
use Filament\Forms\Components\Checkbox;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\View as ViewComponent;
use Filament\Notifications\Notification;
use App\Forms\Components\PaginatedSelect;
use Filament\Forms\Set;
use Filament\Forms\Get;

class ImportLessonMaterial extends Component implements HasForms
{
use InteractsWithForms;

public $course;

public $lesson;

public ?array $data = [
'org_library_access_authorization_id' => null,
'general_topic_id' => null,
'special_topic_id' => null,
'learning_object_id' => null,
'pagination' => [
'general_topic_id' => ['current_page' => 0, 'last_page' => 0, 'total' => 0, 'data' => [], 'search' => null],
'special_topic_id' => ['current_page' => 0, 'last_page' => 0, 'total' => 0, 'data' => [], 'search' => null],
'learning_object_id' => ['current_page' => 0, 'last_page' => 0, 'total' => 0, 'data' => [], 'search' => null]
],
'materials' => []
];

public function mount($course, $lesson): void
{
$this->course = $course;
$this->lesson = $lesson;
$this->form->fill($this->data);
}

public function form(Form $form): Form
{
return $form
->schema([...form components])
->statePath('data');
}

public function create(): void
{
...create method logic
}

public function render(): View
{
return view('livewire.import-lesson-material');
}
}
and its blade livewire.import-lesson-material:
<div class="flex justify-center">
<form wire:submit.prevent="create">
<x-filament::modal
id="import-lesson-material-modal"
slide-over width="6xl"
:close-by-clicking-away="false"
:close-by-escaping="false"
>
<x-slot name="trigger">
<x-filament::button x-on:click="isOpen = true" color="gray">
Add Material
</x-filament::button>
</x-slot>

<x-slot name="header">
Add new material to {{ $lesson['name'] }}
</x-slot>

@error('data.materials.error')
<div style="color: #d90606; border: solid 1px; padding: 5px 15px; border-radius: 8px;">{{ $message }}</div>
@enderror

{{ $this->form }}

<x-slot name="footerActions">
<x-filament::button type="submit">
Import Selection
</x-filament::button>
</x-slot>
</x-filament::modal>
</form>
</div>
<div class="flex justify-center">
<form wire:submit.prevent="create">
<x-filament::modal
id="import-lesson-material-modal"
slide-over width="6xl"
:close-by-clicking-away="false"
:close-by-escaping="false"
>
<x-slot name="trigger">
<x-filament::button x-on:click="isOpen = true" color="gray">
Add Material
</x-filament::button>
</x-slot>

<x-slot name="header">
Add new material to {{ $lesson['name'] }}
</x-slot>

@error('data.materials.error')
<div style="color: #d90606; border: solid 1px; padding: 5px 15px; border-radius: 8px;">{{ $message }}</div>
@enderror

{{ $this->form }}

<x-slot name="footerActions">
<x-filament::button type="submit">
Import Selection
</x-filament::button>
</x-slot>
</x-filament::modal>
</form>
</div>
Mohammed Ouda
Mohammed OudaOP4mo ago
the issue that i get in console when i try to create a new unit or lesson from the Repeater:
No description
toeknee
toeknee4mo ago
please see #❓┊help on how to format code
Mohammed Ouda
Mohammed OudaOP4mo ago
thanks for the note, updated.
toeknee
toeknee4mo ago
IT looks like you are missing the key id of the elements you should prefixed the markers with php i.e. '''php
Mohammed Ouda
Mohammed OudaOP4mo ago
I followed the docs (https://filamentphp.com/docs/3.x/forms/adding-a-form-to-a-livewire-component#adding-the-form) to do what should be done to create a liveware component, idk what i am missing here.
toeknee
toeknee4mo ago
Ok so you are trying to use a Repeater in a Repeater
Mohammed Ouda
Mohammed OudaOP4mo ago
I am using 3 nasted repeaters, but they are working fine. Even if i use the ImportMaterialComponent without imorting the @liveware in its blade, it works fine. I believe that the issue is somewhere in the liveware component and they way of using it inside the form.
toeknee
toeknee4mo ago
Does that livewire component render multiple times in the repeater? The elements I beleive likely need key in that component which is why it can't find the id
Mohammed Ouda
Mohammed OudaOP4mo ago
Yes, it renders with each lesson. And the page has multiples.
toeknee
toeknee4mo ago
https://livewire.laravel.com/docs/understanding-nesting Which would be wire:id="123" and the ID would be that of the lesson id
Laravel
Nesting | Laravel
A full-stack framework for Laravel that takes the pain out of building dynamic UIs.
Mohammed Ouda
Mohammed OudaOP4mo ago
i used it here, but filament overrides these ids with other random ones. any thoughts? idk if the issue is really a duplicated IDs
<div class="flex justify-center" wire:id="{{ $lesson['id'] }}">
<form wire:submit.prevent="create">
<x-filament::modal
id="import-lesson-material-modal"
slide-over width="6xl"
:close-by-clicking-away="false"
:close-by-escaping="false"
>
...rest of blade code
<div class="flex justify-center" wire:id="{{ $lesson['id'] }}">
<form wire:submit.prevent="create">
<x-filament::modal
id="import-lesson-material-modal"
slide-over width="6xl"
:close-by-clicking-away="false"
:close-by-escaping="false"
>
...rest of blade code
Steve
Steve4mo ago
I personally would suspect it's somethign arround binding state as this would generally hint to me that it tried to update a component but couldn't becuase it lost track of it. https://filamentphp.com/docs/3.x/forms/fields/custom#obeying-state-binding-modifiers But I am not really a hero in livewire so afraid I can't help further.
Want results from more Discord servers?
Add your server