Skip to content
全部文档

自定义字段

简介

Livewire 组件是 PHP 类,其状态保存在用户浏览器中。发起网络请求时,状态会发送到服务器,并填充到 Livewire 组件类的公共属性中,访问方式与 PHP 中其他类属性相同。

假设 Livewire 组件有一个名为 $name 的公共属性。可通过两种方式将其绑定到组件 HTML 中的输入框:使用 wire:model 属性,或通过 entangle 与 Alpine.js 属性关联:

blade
<x-dynamic-component
    :component="$getFieldWrapperView()"
    :field="$field"
>
    <input wire:model="name" />
    
    <!-- Or -->
    
    

        <input x-model="state" />
    

</x-dynamic-component>

用户在输入框中输入时,Livewire 组件类中的 $name 属性会更新。提交表单时,$name 会发送到服务器并保存。

这就是 Filament 中字段的工作基础。每个字段对应 Livewire 组件类中的一个公共属性,字段状态就保存在那里。我们称该属性名为字段的「状态路径(state path)」。可在字段视图中用 $getStatePath() 函数获取状态路径:

blade
<x-dynamic-component
    :component="$getFieldWrapperView()"
    :field="$field"
>
    <input wire:model="{{ $getStatePath() }}" />

    <!-- Or -->
    
    

        <input x-model="state" />
    

</x-dynamic-component>

若组件大量依赖第三方库,建议通过 Filament 资源系统异步加载 Alpine.js 组件,这样仅在需要时加载,而非每次页面加载都引入。具体做法见 资源文档

自定义字段类

你可以创建自己的自定义字段类与视图,在项目中复用,甚至作为插件发布给社区。

可用以下命令创建自定义字段类与视图:

bash
php artisan make:filament-form-field LocationPicker

这将创建如下组件类:

php
use Filament\Forms\Components\Field;

class LocationPicker extends Field
{
    protected string $view = 'filament.forms.components.location-picker';
}

同时会在 resources/views/filament/forms/components/location-picker.blade.php 创建视图文件。

INFO

Filament 表单字段不是 Livewire 组件。在表单字段类上定义公共属性与方法,并不会使其在 Blade 视图中可访问。

在 Blade 视图中访问另一组件的状态

在 Blade 视图中,可用 $get() 函数访问 schema 中另一组件的状态:

blade
<x-dynamic-component
    :component="$getFieldWrapperView()"
    :field="$field"
>
    {{ $get('email') }}
</x-dynamic-component>

TIP

除非表单字段是响应式(reactive)的,否则字段值变化时 Blade 视图不会刷新,仅在下次引发服务器请求的用户交互时才会更新。若需对字段值变化做出反应,应使用 live()

在 Blade 视图中访问 Eloquent 记录

在 Blade 视图中,可用 $record 变量访问当前 Eloquent 记录:

blade
<x-dynamic-component
    :component="$getFieldWrapperView()"
    :field="$field"
>
    {{ $record->name }}
</x-dynamic-component>

在 Blade 视图中访问当前操作

在 Blade 视图中,可用 $operation 变量访问当前操作,通常为 createeditview

blade
<x-dynamic-component
    :component="$getFieldWrapperView()"
    :field="$field"
>
    @if ($operation === 'create')
        This is a new conference.
    @else
        This is an existing conference.
    @endif
</x-dynamic-component>

在 Blade 视图中访问当前 Livewire 组件实例

在 Blade 视图中,可用 $this 访问当前 Livewire 组件实例:

blade
@php
    use Filament\Resources\Users\RelationManagers\ConferencesRelationManager;
@endphp

<x-dynamic-component
    :component="$getFieldWrapperView()"
    :field="$field"
>
    @if ($this instanceof ConferencesRelationManager)
        You are editing conferences the of a user.
    @endif
</x-dynamic-component>

在 Blade 视图中访问当前字段实例

在 Blade 视图中,可用 $field 访问当前字段实例。可对该对象调用公共方法,以获取变量中可能没有的其他信息:

blade
<x-dynamic-component
    :component="$getFieldWrapperView()"
    :field="$field"
>
    @if ($field->getState())
        This is a new conference.
    @endif
</x-dynamic-component>

为自定义字段类添加配置方法

可在自定义字段类上添加公共方法:接受配置值、存入受保护属性,再通过另一公共方法返回:

php
use Filament\Forms\Components\Field;

class LocationPicker extends Field
{
    protected string $view = 'filament.forms.components.location-picker';
    
    protected ?float $zoom = null;

    public function zoom(?float $zoom): static
    {
        $this->zoom = $zoom;

        return $this;
    }

    public function getZoom(): ?float
    {
        return $this->zoom;
    }
}

随后,在自定义字段的 Blade 视图中,可用 $getZoom() 函数访问 zoom:

blade
<x-dynamic-component
    :component="$getFieldWrapperView()"
    :field="$field"
>
    {{ $getZoom() }}
</x-dynamic-component>

在自定义字段类上定义的任何公共方法,都可按此方式在 Blade 视图中作为变量函数访问。

向自定义字段类传入配置值时,可使用该公共方法:

php
use App\Filament\Forms\Components\LocationPicker;

LocationPicker::make('location')
    ->zoom(0.5)

在自定义字段配置方法中允许工具注入

工具注入(Utility injection) 是 Filament 的强大功能,允许用户用可访问各种工具的函数配置组件。要允许工具注入,请确保配置的参数类型与属性类型允许传入 Closure。在 getter 方法中,应将配置值传给 $this->evaluate():若用户传入函数则注入工具,若为静态值则直接返回:

php
use Closure;
use Filament\Forms\Components\Field;

class LocationPicker extends Field
{
    protected string $view = 'filament.forms.components.location-picker';
    
    protected float | Closure | null $zoom = null;

    public function zoom(float | Closure | null $zoom): static
    {
        $this->zoom = $zoom;

        return $this;
    }

    public function getZoom(): ?float
    {
        return $this->evaluate($this->zoom);
    }
}

现在可向 zoom() 传入静态值或函数,并注入任意工具作为参数:

php
use App\Filament\Forms\Components\LocationPicker;

LocationPicker::make('location')
    ->zoom(fn (Conference $record): float => $record->isGlobal() ? 1 : 0.5)

遵循状态绑定修饰符

将字段绑定到状态路径时,可使用 defer 修饰符,确保仅在用户提交表单或下次发起 Livewire 请求时才把状态发送到服务器。这是默认行为。

也可对字段使用 live(),使用户与字段交互时立即将状态发送到服务器。文档的响应性章节介绍了许多高级用法。

Filament 提供 $applyStateBindingModifiers() 函数,可在视图中把状态绑定修饰符应用到 wire:model$wire.$entangle() 绑定:

blade
<x-dynamic-component
    :component="$getFieldWrapperView()"
    :field="$field"
>
    <input {{ $applyStateBindingModifiers('wire:model') }}="{{ $getStatePath() }}" />

    <!-- Or -->

    

        <input x-model="state" />
    

</x-dynamic-component>

从 JavaScript 调用字段方法

有时需要在 Blade 视图的 JavaScript 中调用字段类上的方法,例如异步获取数据、处理文件上传或执行服务端计算。Filament 可用 #[ExposedLivewireMethod] 属性将字段类上的方法暴露给 JavaScript。

暴露方法

要将方法暴露给 JavaScript,请在自定义字段类的公共方法上添加 #[ExposedLivewireMethod] 属性:

php
use Filament\Forms\Components\Field;
use Filament\Support\Components\Attributes\ExposedLivewireMethod;

class LocationPicker extends Field
{
    protected string $view = 'filament.forms.components.location-picker';

    #[ExposedLivewireMethod]
    public function geocodeAddress(string $address): array
    {
        // Perform geocoding logic...

        return [
            'latitude' => $latitude,
            'longitude' => $longitude,
        ];
    }
}

INFO

只有标记了 #[ExposedLivewireMethod] 的方法才能从 JavaScript 调用。这是防止任意方法执行的安全措施。

从 JavaScript 调用方法

在 Blade 视图中,可用 $wire.callSchemaComponentMethod() 调用已暴露的方法。第一个参数是组件的 key(可通过 $getKey() 获取),第二个是方法名,第三个参数可传入实参:

blade
@php
    $key = $getKey();
@endphp

<x-dynamic-component
    :component="$getFieldWrapperView()"
    :field="$field"
>
    

        <input type="text" x-model="address" />
        <button type="button" x-on:click="geocode">Geocode</button>

        <template x-if="coordinates">
            <p x-text="`${coordinates.latitude}, ${coordinates.longitude}`"></p>
        </template>
    

</x-dynamic-component>

防止重新渲染

默认情况下,调用已暴露方法会触发 Livewire 组件重新渲染。若方法无需更新 UI,可在 #[ExposedLivewireMethod] 旁加上 Livewire 的 #[Renderless] 属性以跳过重新渲染:

php
use Filament\Forms\Components\Field;
use Filament\Support\Components\Attributes\ExposedLivewireMethod;
use Livewire\Attributes\Renderless;

class LocationPicker extends Field
{
    protected string $view = 'filament.forms.components.location-picker';

    #[ExposedLivewireMethod]
    #[Renderless]
    public function geocodeAddress(string $address): array
    {
        // ...
    }
}