From 598984f2914163a39f25be64233081153e814fa7 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Tue, 14 Oct 2025 10:32:49 +0200 Subject: [PATCH] Fix wire:model warnings and ensure truly unique HTML IDs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Problems Fixed:** 1. Livewire warnings about non-existent properties (e.g., wire:model="dcgoowgw0gcgcsgg00c8kskc") 2. Duplicate HTML IDs still appearing despite initial fix **Root Causes:** 1. Auto-generated Cuid2 IDs were being used for wire:model when no explicit id was provided 2. Livewire's wire:id attribute isn't available during server-side rendering **Solutions:** 1. Set $modelBinding to 'null' (string) when id is not provided, preventing invalid wire:model generation 2. Use random MD5 suffix instead of Livewire component ID for guaranteed uniqueness during initial render 3. Maintain correct $name attribute based on original property name **Technical Changes:** - Input, Textarea, Select, Datalist: Use random 8-char suffix for uniqueness - Checkbox: Apply same random suffix approach - wire:model now only created for explicit property names - HTML IDs are unique from initial server render (no hydration required) **Result:** ✅ No more Livewire property warnings ✅ Truly unique HTML IDs across all components ✅ wire:model bindings work correctly ✅ Validation and form submission unaffected 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app/View/Components/Forms/Checkbox.php | 12 ++++++------ app/View/Components/Forms/Datalist.php | 16 +++++++++------- app/View/Components/Forms/Input.php | 17 ++++++++++------- app/View/Components/Forms/Select.php | 16 +++++++++------- app/View/Components/Forms/Textarea.php | 16 +++++++++------- 5 files changed, 43 insertions(+), 34 deletions(-) diff --git a/app/View/Components/Forms/Checkbox.php b/app/View/Components/Forms/Checkbox.php index d96e385f7..a759164fb 100644 --- a/app/View/Components/Forms/Checkbox.php +++ b/app/View/Components/Forms/Checkbox.php @@ -53,14 +53,14 @@ public function render(): View|Closure|string { // Store original ID for wire:model binding (property name) $this->modelBinding = $this->id; - $this->htmlId = $this->id; - // Generate unique HTML ID by prefixing with Livewire component ID if available + // Generate unique HTML ID by adding random suffix + // This prevents duplicate IDs when multiple forms are on the same page if ($this->id) { - $livewireId = $this->attributes?->wire('id'); - if ($livewireId) { - $this->htmlId = $livewireId.'-'.$this->id; - } + $uniqueSuffix = substr(md5(uniqid((string) mt_rand(), true)), 0, 8); + $this->htmlId = $this->id.'-'.$uniqueSuffix; + } else { + $this->htmlId = $this->id; } return view('components.forms.checkbox'); diff --git a/app/View/Components/Forms/Datalist.php b/app/View/Components/Forms/Datalist.php index e5bbbfb5c..08a320f68 100644 --- a/app/View/Components/Forms/Datalist.php +++ b/app/View/Components/Forms/Datalist.php @@ -56,20 +56,22 @@ public function render(): View|Closure|string if (is_null($this->id)) { $this->id = new Cuid2; - $this->modelBinding = $this->id; + // Don't create wire:model binding for auto-generated IDs + $this->modelBinding = 'null'; } - // Generate unique HTML ID by prefixing with Livewire component ID + // Generate unique HTML ID by adding random suffix // This prevents duplicate IDs when multiple forms are on the same page - $livewireId = $this->attributes?->wire('id'); - if ($livewireId && $this->modelBinding) { - $this->htmlId = $livewireId.'-'.$this->modelBinding; + if ($this->modelBinding && $this->modelBinding !== 'null') { + // Use original ID with random suffix for uniqueness + $uniqueSuffix = substr(md5(uniqid((string) mt_rand(), true)), 0, 8); + $this->htmlId = $this->modelBinding.'-'.$uniqueSuffix; } else { - $this->htmlId = $this->modelBinding ?: $this->id; + $this->htmlId = (string) $this->id; } if (is_null($this->name)) { - $this->name = $this->modelBinding; + $this->name = $this->modelBinding !== 'null' ? $this->modelBinding : (string) $this->id; } return view('components.forms.datalist'); diff --git a/app/View/Components/Forms/Input.php b/app/View/Components/Forms/Input.php index 37c126c0e..9a0c87c0a 100644 --- a/app/View/Components/Forms/Input.php +++ b/app/View/Components/Forms/Input.php @@ -52,19 +52,22 @@ public function render(): View|Closure|string if (is_null($this->id)) { $this->id = new Cuid2; - $this->modelBinding = $this->id; + // Don't create wire:model binding for auto-generated IDs + $this->modelBinding = 'null'; } - // Generate unique HTML ID by prefixing with Livewire component ID + + // Generate unique HTML ID by adding random suffix // This prevents duplicate IDs when multiple forms are on the same page - $livewireId = $this->attributes?->wire('id'); - if ($livewireId && $this->modelBinding) { - $this->htmlId = $livewireId.'-'.$this->modelBinding; + if ($this->modelBinding && $this->modelBinding !== 'null') { + // Use original ID with random suffix for uniqueness + $uniqueSuffix = substr(md5(uniqid((string) mt_rand(), true)), 0, 8); + $this->htmlId = $this->modelBinding.'-'.$uniqueSuffix; } else { - $this->htmlId = $this->modelBinding ?: $this->id; + $this->htmlId = (string) $this->id; } if (is_null($this->name)) { - $this->name = $this->modelBinding; + $this->name = $this->modelBinding !== 'null' ? $this->modelBinding : (string) $this->id; } if ($this->type === 'password') { $this->defaultClass = $this->defaultClass.' pr-[2.8rem]'; diff --git a/app/View/Components/Forms/Select.php b/app/View/Components/Forms/Select.php index c0811b5bd..54d83ded7 100644 --- a/app/View/Components/Forms/Select.php +++ b/app/View/Components/Forms/Select.php @@ -49,20 +49,22 @@ public function render(): View|Closure|string if (is_null($this->id)) { $this->id = new Cuid2; - $this->modelBinding = $this->id; + // Don't create wire:model binding for auto-generated IDs + $this->modelBinding = 'null'; } - // Generate unique HTML ID by prefixing with Livewire component ID + // Generate unique HTML ID by adding random suffix // This prevents duplicate IDs when multiple forms are on the same page - $livewireId = $this->attributes?->wire('id'); - if ($livewireId && $this->modelBinding) { - $this->htmlId = $livewireId.'-'.$this->modelBinding; + if ($this->modelBinding && $this->modelBinding !== 'null') { + // Use original ID with random suffix for uniqueness + $uniqueSuffix = substr(md5(uniqid((string) mt_rand(), true)), 0, 8); + $this->htmlId = $this->modelBinding.'-'.$uniqueSuffix; } else { - $this->htmlId = $this->modelBinding ?: $this->id; + $this->htmlId = (string) $this->id; } if (is_null($this->name)) { - $this->name = $this->modelBinding; + $this->name = $this->modelBinding !== 'null' ? $this->modelBinding : (string) $this->id; } return view('components.forms.select'); diff --git a/app/View/Components/Forms/Textarea.php b/app/View/Components/Forms/Textarea.php index cad85e167..e962efc29 100644 --- a/app/View/Components/Forms/Textarea.php +++ b/app/View/Components/Forms/Textarea.php @@ -62,20 +62,22 @@ public function render(): View|Closure|string if (is_null($this->id)) { $this->id = new Cuid2; - $this->modelBinding = $this->id; + // Don't create wire:model binding for auto-generated IDs + $this->modelBinding = 'null'; } - // Generate unique HTML ID by prefixing with Livewire component ID + // Generate unique HTML ID by adding random suffix // This prevents duplicate IDs when multiple forms are on the same page - $livewireId = $this->attributes?->wire('id'); - if ($livewireId && $this->modelBinding) { - $this->htmlId = $livewireId.'-'.$this->modelBinding; + if ($this->modelBinding && $this->modelBinding !== 'null') { + // Use original ID with random suffix for uniqueness + $uniqueSuffix = substr(md5(uniqid((string) mt_rand(), true)), 0, 8); + $this->htmlId = $this->modelBinding.'-'.$uniqueSuffix; } else { - $this->htmlId = $this->modelBinding ?: $this->id; + $this->htmlId = (string) $this->id; } if (is_null($this->name)) { - $this->name = $this->modelBinding; + $this->name = $this->modelBinding !== 'null' ? $this->modelBinding : (string) $this->id; } // $this->label = Str::title($this->label);