laravel-livewire

Why have Uncaught Snapshot missing error with slot components of 2 types?


On laravel 11 site I use on the form volt components defined as resources/views/livewire/common/controls/input_text.blade.php :

<?php

use Livewire\Volt\Component;
use Livewire\Attributes\Modelable;

new class extends Component {
    #[Modelable]
    public $form;
    public string $formattedValue = '';
    public string $fieldName = '';
    public string $label = '';
    public ?string $placeholder = '';
    public ?int $maxlength = 0;
    public ?int $tabindex = 0;
};
?>

<div class="editor_field_block_wrapper d2">

    <div class="">
        <div class="w-4/12 pb-0 pl-2 md:pt-3 ">
            <label for="{{ $fieldName }}" class="">
                {{ !empty($label) ? $label: \Str::ucfirst(\Str::replace('_', ' ', $fieldName)) }}:
            </label>
        </div>
        <div class="p-2 w-full">
            <input id="{{ $fieldName }}" name="{{ $fieldName }}" type="text"
                   class="editor_form_input"
                   @if(!empty($maxlength)) maxlength="{{ $maxlength }}" @endif
                   wire:model="form.{{ $fieldName }}"
                   autocomplete="off"
                   @if(!empty($placeholder)) placeholder="{{ $placeholder }}" @endif
                   @if(!empty($tabindex)) tabindex="{{ $tabindex }}" @endif/>
            @error('form.' . $fieldName)
            <span class="error_text">{{$message}}</span>
            @enderror
        </div>
    </div>
</div>

and resources/views/livewire/common/controls/input_readonly.blade.php (I tried to use :key= in any "div" element like I read at https://livewire.laravel.com/docs/troubleshooting#adding-wirekey ):

<?php

use Livewire\Volt\Component;
use function Livewire\Volt\{mount, state};

state(['fieldName'=> '', 'form'=> '']);
new class extends Component {
    public $form = [];
    public $fieldName = '';

} ?>

<div class="editor_field_block_wrapper d1"  :key="$fieldName . 'RootDivReadonly'">


    <div class=""  :key="$fieldName . 'SplitterDivReadonly'">
        <div class="w-4/12 pb-0 pl-2 md:pt-3">
            <label for="{{ $fieldName }}" class="">
                {{ \Str::ucfirst($fieldName) }}:
            </label>
        </div>
        <div class="p-2 w-full"  :key="$fieldName . 'ParentDivReadonly'">
            <input
                id="{{ $fieldName }}" name="{{ $fieldName }}" type="text"
                value="{{ $form->{$fieldName} }}"
                tabindex="-1"
                :key="$fieldName . 'Readonly'"
                readonly/>
        </div>
    </div>
</div>

on the form I se both type controls :

<form class="form-editor" wire:submit="update" enctype="multipart/form-data">


    <livewire:common.controls.input_readonly fieldName="id" :form="$form" :key="'idReadonly'"  />


    <livewire:common.controls.input_text fieldName="phone" wire:model="form" :maxlength="100" tabindex="10" :key="'phoneInputText'" />

    <livewire:common.controls.input_text fieldName="website" wire:model="form" :maxlength="50" tabindex="20" placeholder="Fill your website url" :key="'websiteInputText'" />


</form>

Problem is that after I edit data in input_text elements and save the form I got error :

in Uncaught Snapshot missing on Livewire component with id: 2MfhLF0gKp6I1w1LtNDh

and my form is broken.

If I remove line with

<livewire:common.controls.input_readonly
    

component (it is the only on the form) - I have no this error and form is saved ok.

What is wrong in my code and how to fix it ?

"laravel/framework": "^11.9",
"livewire/livewire": "^3.5",
"livewire/volt": "^1.6",

UPDATES :

I remade resources/views/livewire/common/controls/input_readonly.blade.php file :

<?php

use function Livewire\Volt\{state};

state(['fieldName'=> '', 'form'=> '']);

?>

<div wire:key="$fieldName . 'RootDivReadonly'">
    
    <div   wire:key="$fieldName . 'SplitterDivReadonly'">
        <div class="w-4/12 pb-0 pl-2 md:pt-3">
            <label for="{{ $fieldName }}" >
                {{ \Str::ucfirst($fieldName) }}:
            </label>
        </div>
        <div class="p-2 w-full"  wire:key="$fieldName . 'ParentDivReadonly'">
            <input
                id="{{ $fieldName }}" name="{{ $fieldName }}" type="text"
                value="{{ $form->{$fieldName} }}"
                tabindex="-1"
                wire:key="$fieldName . 'Readonly'"
                readonly/>
        </div>
    </div>
</div>

But still got the same error when I save the form. No error on page opened. Did I define wire:key correctly ?

Please provide a link to functional API manuals...

UPDATES 2:

Here https://livewire.laravel.com/docs/alpine#refreshing-a-component I found :

You can easily refresh a Livewire component (trigger network roundtrip to re-render a component's Blade view) using $wire.$refresh():

I try to use it as in my blade file I have :

    <form class="form-editor" wire:submit="update" enctype="multipart/form-data">


    <livewire:common.controls.input_readonly fieldName="id" :form="$form" :key="'idReadonly'"  />


    <livewire:common.controls.input_text fieldName="phone" wire:model="form" :maxlength="100" tabindex="10" :key="'phoneInputText'" />

    <livewire:common.controls.input_text fieldName="website" wire:model="form" :maxlength="50" tabindex="20" placeholder="Fill your website url" :key="'websiteInputText'" />

</form>
I tried to use $refresh method in the parent livewire component :


use Livewire\Component;

class ProfileEditor extends Component
{

    public function update(Request $request)
    {

        try {
            $user = User::findOrFail($this->form->id);
        } catch (ModelNotFoundException $e) {
            return;
        }

        $user->phone = $this->form->phone;
        $user->website = $this->form->website;
        ...

        try {
            DB::beginTransaction();
            $user->save();
            DB::commit();

            $this->refresh(); // I added this method.

But I got runtime error :

Run time error : 

Method App\Livewire\Personal\ProfileEditor::refresh does not exist.

How to use this method, as in blade file the form is submitted with " wire:submit="update" instruction ?

UPDATES 3:

I try to use both ways :

  1. In component ProfileEditor I dispatched the event after data are saved:

    $user->save();
    $this->dispatch('profileEditorSaved', ['id' => $this->form->id]);

and in blade file I catch this event :

<div>
        ...
                    <form class="form-editor" wire:submit="update" enctype="multipart/form-data">
                        <livewire:common.controls.input_readonly fieldName="id" :form="$form" :key="'idReadonly'"  />

                        <livewire:common.controls.input_text fieldName="phone" wire:model="form" :maxlength="100" tabindex="10" :key="'phoneInputText'" />

                        <livewire:common.controls.input_text fieldName="website" wire:model="form" :maxlength="50" tabindex="20" placeholder="Fill your website url" :key="'websiteInputText'" />
                        ...
                    </form>


    @script
    <script>


        // This event listener updates the component with all childs
        $wire.on("profileEditorSaved", ($options) => {
            console.log('profileEditorSaved $options::')
            console.log($options)

            $wire.$refresh();
        });

    </script>
    @endscript


</div>

After data are saved I see in the browser's console output of profileEditorSaved event, but next the same error :

Uncaught Snapshot missing on Livewire component with id: VNoy1hE2mxNE9ewZ4T5O
  1. Not shure how to implement it. I tried to modify input_readonly.blade.php as :

    use function Livewire\Volt{state};
    state(['fieldName'=> '', 'form'=> '']);

     <div class="editor_field_block_device_splitter"  wire:key="SplitterDivReadonly-{{rand()}}">
         <div class="w-4/12 pb-0 pl-2 md:pt-3">
             <label for="{{ $fieldName }}" class="editor_field_block_device_label">
                 {{ \Str::ucfirst($fieldName) }}:
             </label>
         </div>
         <div class="p-2 w-full"  wire:key="ParentDivReadonly-{{rand()}}">
             <input
                 id="{{ $fieldName }}" name="{{ $fieldName }}" type="text"
                 class="editor_form_readonly"
                 value="{{ $form->{$fieldName} }}"
                 tabindex="-1"
                 wire:key="Readonly-{{rand()}}"
                 readonly/>
         </div>
     </div>
    

But still the same error!

Did I understand you correctly!

Thanks in advance!


Solution

  • I think the problem might be related to mixed Volt API. To declare variable you should either use functional API:

    state(['fieldName'=> '', 'form'=> '']);
    

    Or class API:

    new class extends Component {
        public $form = [];
        public $fieldName = '';
    }
    

    Do not mix this, because it's completely different approaches. Functional API creates class under the hood, while with traditional class-based components you declare it explicit.

    Also, you shouldn't use :key, but wire:key with html elements. For example

    <livewire:some-component :key="unique-component-key"/>
    
    <div wire:key="unique-key"></div>
    

    Update. I checked your demo app. When I removed all wire:key attributes and refresh event listener, the issue is gone.

    <?php
    
    use function Livewire\Volt\{state};
    
    state(['fieldName' => '', 'form' => '']);
    
    ?>
    
    <div class="">
        <div class="">
            <div class="w-4/12 pb-0 pl-2 md:pt-3">
                <label for="{{ $fieldName }}" class="">
                    {{ \Str::ucfirst($fieldName) }}:
                </label>
            </div>
            <div class="p-2 w-full">
                <input
                    id="{{ $fieldName }}" name="{{ $fieldName }}" type="text"
                    style="border:1px dotted"
                    value="{{ $form->{$fieldName} }}"
                    tabindex="-1"
                    readonly
                />
            </div>
        </div>
    </div>
    

    You can also remove dispatching event from your component.

    $this->dispatch('saved');