Laravel Livewire分类组件异常:输入价格后仅显示父分类
分类嵌套展示异常:输入价格后仅显示父分类的解决方法
问题描述
实现了分类及其子分类的嵌套展示,基础功能正常,但输入最低价格或最高价格触发页面更新时,仅显示父分类,子分类消失。
现有代码
sidebar.blade.php
<div class="bg-white p-6 border border-border-white rounded-md hidden md:flex md:flex-col md:gap-6 md:max-w-[270px] h-fit"> <div> <p class="capitalize font-semibold">type</p> <ul class="mt-3 flex flex-col gap-1"> <li class="flex items-center gap-2"> <input type="radio" id="any" wire:model="type" class="accent-purple w-4 h-4" value="any" wire:click="type"> <label for="any" class="capitalize">any</label> </li> <li class="flex items-center gap-2"> <input type="radio" id="auction" wire:model="type" class="accent-purple w-4 h-4" value="auction" wire:click="type"> <label for="auction" class="capitalize">Auction</label> </li> <li class="flex items-center gap-2"> <input type="radio" id="classified" wire:model="type" class="accent-purple w-4 h-4" value="classified" wire:click="type"> <label for="classified" class="capitalize">classified</label> </li> selected {{$type}} </ul> </div> <div> <p class="capitalize font-semibold">category</p> <ul class="mt-3 flex flex-col gap-1"> <li class="flex items-center gap-2"> <input type="radio" id="any" wire:model="category" class="accent-purple w-4 h-4" wire:click="category" value="0"> <label for="any" class="capitalize">any</label> </li> @foreach ($categories as $category) <li class="flex items-center gap-2"> <input type="radio" id="classified" wire:model="category" class="accent-purple w-4 h-4" wire:click="category" value="{{ $category->id}}"> <label for="classified" class="capitalize">{{ __($category->name) }}</label> </li> @if(!is_null($category->subcategories)) <ul class="mt-1 ml-5 flex flex-col gap-1"> @foreach ($category->subcategories as $subcategory) <li class="flex items-center gap-2"> <input type="radio" id="apperal" wire:model="category" class="accent-purple w-4 h-4" wire:click="category" value="{{ $subcategory->id}}"> <label for="apperal" class="capitalize">{{ __($subcategory->name) }}</label> </li> @endforeach </ul> @endif @endforeach </ul> </div> <div> <p class="capitalize font-semibold">price</p> <input type="number" wire:model="min_price" wire:model.live="min_price" id="min_price" placeholder="Min Price" class="mt-3 w-full border border-border-white rounded-md p-4 focus:outline-none focus:border-purple"> @error('min_price') <p class="text-sm mt-1 text-red-500">{{ $message}}</p> @enderror <input type="number" wire:model="max_price" wire:model.live="max_price" id="max_price" placeholder="Max Price" class="mt-1 w-full border border-border-white rounded-md p-4 focus:outline-none focus:border-purple"> </div> </div>
Sidebar.php
<?php namespace App\Livewire\Listings; use Livewire\Component; use App\Models\Category; use Livewire\Attributes\Url; use Livewire\Attributes\Validate; class Sidebar extends Component { #[Url] public $type = 'any'; #[Url] public $category = 'any'; #[Url, Validate] public $min_price = ''; #[Url] public $max_price = ''; public $categories; public function mount() { $this->categories = $this->getNestedCategories(); } private function getNestedCategories($parentId = null) { $categories = Category::where('parent_id', $parentId)->get(); foreach ($categories as $category) { $category->subcategories = $this->getNestedCategories($category->id); } return $categories; } public function rules() { return [ 'min_price' => 'numeric|between:1,999999999', 'max_price' => 'numeric|between:1,999999999', ]; } public function render() { return view('livewire.listings.sidebar', [ 'categories' => $this->categories, ]); } }
Category.php
<?php namespace App\Models; use App\Status; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; class Category extends Model { use HasFactory; public function Listings(): HasMany { return $this->hasMany(Listing::class, 'category_id'); } public function child(): HasMany { return $this->hasMany(Category::class, 'parent_id'); } public function parent(): BelongsTo { return $this->belongsTo(Category::class, 'parent_id'); } protected $casts = [ 'is_active' => Status::class, ]; }
问题原因
- 分类数据未实时加载:
$categories仅在mount()方法初始化,价格更新触发组件重新渲染时,嵌套的subcategories数据因Livewire序列化丢失,无法正常展示。 - DOM元素ID重复:多个单选框使用相同的
id(如classified、apperal),导致DOM解析异常,影响组件交互和渲染。
解决步骤
1. 修复Livewire组件数据加载逻辑
将分类数据改为计算属性,确保每次渲染都能重新获取完整的嵌套分类:
<?php namespace App\Livewire\Listings; use Livewire\Component; use App\Models\Category; use Livewire\Attributes\Url; use Livewire\Attributes\Validate; use Livewire\Attributes\Computed; class Sidebar extends Component { #[Url] public $type = 'any'; #[Url] public $category = 'any'; #[Url, Validate] public $min_price = ''; #[Url] public $max_price = ''; public function rules() { return [ 'min_price' => 'numeric|between:1,999999999', 'max_price' => 'numeric|between:1,999999999', ]; } #[Computed] public function categories() { return $this->getNestedCategories(); } private function getNestedCategories($parentId = null) { $categories = Category::where('parent_id', $parentId)->get(); foreach ($categories as $category) { $category->subcategories = $this->getNestedCategories($category->id); } return $categories; } public function render() { return view('livewire.listings.sidebar'); } }
2. 修复Blade模板重复ID问题
给每个单选框设置唯一ID,确保与label一一对应:
<div class="bg-white p-6 border border-border-white rounded-md hidden md:flex md:flex-col md:gap-6 md:max-w-[270px] h-fit"> <div> <p class="capitalize font-semibold">type</p> <ul class="mt-3 flex flex-col gap-1"> <li class="flex items-center gap-2"> <input type="radio" id="type-any" wire:model="type" class="accent-purple w-4 h-4" value="any"> <label for="type-any" class="capitalize">any</label> </li> <li class="flex items-center gap-2"> <input type="radio" id="type-auction" wire:model="type" class="accent-purple w-4 h-4" value="auction"> <label for="type-auction" class="capitalize">Auction</label> </li> <li class="flex items-center gap-2"> <input type="radio" id="type-classified" wire:model="type" class="accent-purple w-4 h-4" value="classified"> <label for="type-classified" class="capitalize">classified</label> </li> selected {{$type}} </ul> </div> <div> <p class="capitalize font-semibold">category</p> <ul class="mt-3 flex flex-col gap-1"> <li class="flex items-center gap-2"> <input type="radio" id="category-any" wire:model="category" class="accent-purple w-4 h-4" value="0"> <label for="category-any" class="capitalize">any</label> </li> @foreach ($categories as $category) <li class="flex items-center gap-2"> <input type="radio" id="category-{{ $category->id }}" wire:model="category" class="accent-purple w-4 h-4" value="{{ $category->id}}"> <label for="category-{{ $category->id }}" class="capitalize">{{ __($category->name) }}</label> </li> @if($category->subcategories->isNotEmpty()) <ul class="mt-1 ml-5 flex flex-col gap-1"> @foreach ($category->subcategories as $subcategory) <li class="flex items-center gap-2"> <input type="radio" id="category-{{ $subcategory->id }}" wire:model="category" class="accent-purple w-4 h-4" value="{{ $subcategory->id}}"> <label for="category-{{ $subcategory->id }}" class="capitalize">{{ __($subcategory->name) }}</label> </li> @endforeach </ul> @endif @endforeach </ul> </div> <div> <p class="capitalize font-semibold">price</p> <input type="number" wire:model.live="min_price" id="min_price" placeholder="Min Price" class="mt-3 w-full border border-border-white rounded-md p-4 focus:outline-none focus:border-purple"> @error('min_price') <p class="text-sm mt-1 text-red-500">{{ $message}}</p> @enderror <input type="number" wire:model.live="max_price" id="max_price" placeholder="Max Price" class="mt-1 w-full border border-border-white rounded-md p-4 focus:outline-none focus:border-purple"> </div> </div>
3. 优化Category模型关联(可选)
定义subcategories关联,简化递归查询逻辑:
<?php namespace App\Models; use App\Status; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; class Category extends Model { use HasFactory; public function listings(): HasMany { return $this->hasMany(Listing::class, 'category_id'); } public function subcategories(): HasMany { return $this->hasMany(Category::class, 'parent_id'); } public function parent(): BelongsTo { return $this->belongsTo(Category::class, 'parent_id'); } protected $casts = [ 'is_active' => Status::class, ]; }
然后修改组件的getNestedCategories方法:
private function getNestedCategories($parentId = null) { return Category::where('parent_id', $parentId)->with('subcategories')->get(); }
内容的提问来源于stack exchange,提问作者Dilhan
相关产品推荐
相关产品推荐

