You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel Filament重置Select组件后占位符丢失问题求助

FilamentPHP Select组件重置后占位符消失问题

问题描述

使用FilamentPHP开发表单时,遇到以下问题:当通过type_task_id组件的afterStateUpdated方法重置product_id等关联Select组件的状态后,被重置的Select组件的占位符会消失。表单其余功能均正常,此前采用Filament官方的Select选项渲染HTML方案未出现异常,推测可能是组件间重载冲突、Choice.js相关问题或重置时HTML加载异常导致。

相关代码片段

Forms\Components\Select::make('extendedProps.type_task_id')
    ->label('Type de tâche')
    //->required()
    ->allowHtml()
    ->searchable()
    // 加载初始选项
    ->options(function(Closure $get) {
        $types = collect([]);
        if(!empty($get('extendedProps.team_id'))) {
            $types = TypeTask::where('team_id', $get('extendedProps.team_id'))->get();
        }
        
        return $types->mapWithKeys(function ($type) {
            return [$type->getKey() => static::getSelectIcon($type)];
        })->toArray();
    })
    // 搜索时加载选项
    ->getSearchResultsUsing(function (Closure $get, string $search) {
        $types = collect([]);
        if(!empty($get('extendedProps.team_id'))) {
            $types = TypeTask::where('team_id', $get('extendedProps.team_id'))
                ->where('name', 'like', "%{$search}%")
                ->limit(50)
                ->get();
        }
        
        return $types->mapWithKeys(function ($type) {
            return [$type->getKey() => static::getSelectIcon($type)];
        })->toArray();
    })
    ->reactive()
    ->preload()
    ->columnSpan(12)
    // 选择后重置关联组件
    ->afterStateUpdated(function (Closure $set, Closure $get, $state) {
        if ($get('extendedProps.product_id') !== null) {
            $set('extendedProps.product_id', null);
        }
        if ($get('extendedProps.device_id') !== null) {
            $set('extendedProps.device_id', null);
        }
    })
    ->extraAttributes(['class' => 'coucou-test'])
    ->placeholder('Libre')
    ->hidden(function(Closure $get) {
        $types = collect([]);
        if(!empty($get('extendedProps.team_id'))) {
            $types = TypeTask::where('team_id', $get('extendedProps.team_id'))->get();
    
            if(count($types) == 0) {
                return true;
            }
        }
        else {
            return true;
        }
    
        return false;
    }),

Forms\Components\Select::make('extendedProps.product_id')
    ->label('Produit')
    ->required()
    ->allowHtml()
    ->searchable()
    // 加载初始选项
    ->options(function(Closure $get) {
        $products = collect([]);
        if(in_array($get('extendedProps.type_task_id'), [1, 2])) { // Brassage + Conditionnement
            $products = Product::where('is_crafted', 1)->get();
        }
        else { // 其他类型
            $products = Product::where('is_crafted', 0)->get();
        }
        
        return $products->mapWithKeys(function ($product) {
            return [$product->getKey() => static::getCleanOptionStringProduct($product)];
        })->toArray();
    })
    // 搜索时加载选项
    ->getSearchResultsUsing(function (Closure $get, string $search) {
        $products = collect([]);
        if(in_array($get('extendedProps.type_task_id'), [1, 2])) { // Brassage + Conditionnement
            $products = Product::where('is_crafted', 1);
        }
        else { // 其他类型
            $products = Product::where('is_crafted', 0);
        }
        $products = $products->where('name', 'like', "%{$search}%")
            ->limit(50)
            ->get();
        
        return $products->mapWithKeys(function ($product) {
            return [$product->getKey() => static::getCleanOptionStringProduct($product)];
        })->toArray();
    })
    ->reactive()
    ->preload()
    ->columnSpan(6)
    ->hidden(function(Closure $get) {
        return $get('extendedProps.type_task_id') === null;
    })
    ->placeholder('Libre'),

问题原因分析

  1. allowHtml与占位符渲染冲突:启用allowHtml()后,Filament底层依赖的Choice.js会将选项内容作为HTML解析,但重置状态为null时,组件未正确将纯文本占位符插入DOM,而是可能尝试解析空内容,导致占位符消失。
  2. 状态重置未触发DOM更新:直接通过$set将状态设为null仅更新了表单数据模型,但未触发Select组件的DOM重新渲染流程,占位符未被重新初始化。
  3. 隐藏/显示切换的状态不同步:product_id组件的hidden状态依赖type_task_id,当type_task_id变更后,product_id从隐藏变为显示,此时重置状态可能未同步触发占位符的渲染逻辑。

解决方案

方案1:使用空字符串替代null重置状态

将重置时的null改为空字符串,触发组件的正常渲染流程:

->afterStateUpdated(function (Closure $set, Closure $get, $state) {
    if ($get('extendedProps.product_id') !== null) {
        $set('extendedProps.product_id', ''); // 用空字符串替代null
    }
    if ($get('extendedProps.device_id') !== null) {
        $set('extendedProps.device_id', '');
    }
})

方案2:将占位符改为HTML格式

既然启用了allowHtml(),统一占位符的HTML格式,确保组件正确解析:

->placeholder('<span>Libre</span>')

方案3:自定义JS监听重置事件

通过自定义JavaScript监听Livewire更新事件,手动恢复占位符:

document.addEventListener('livewire:update', function () {
    document.querySelectorAll('.choices').forEach(el => {
        const instance = el.Choice;
        if (instance && instance.input.value === '') {
            const placeholder = instance.container.querySelector('.choices__placeholder');
            if (placeholder) placeholder.textContent = 'Libre';
        }
    });
});

方案4:替换hidden为disabled逻辑

避免组件频繁隐藏/显示导致的状态异常,改为禁用组件而非隐藏:

->disabled(function(Closure $get) {
    return $get('extendedProps.type_task_id') === null;
})

内容的提问来源于stack exchange,提问作者Antoine My

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 21:24:54