自定义字段
简介
Livewire 组件是 PHP 类,其状态保存在用户浏览器中。发起网络请求时,状态会发送到服务器,并填充到 Livewire 组件类的公共属性中,访问方式与 PHP 中其他类属性相同。
假设 Livewire 组件有一个名为 $name 的公共属性。可通过两种方式将其绑定到组件 HTML 中的输入框:使用 wire:model 属性,或通过 entangle 与 Alpine.js 属性关联:
<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() 函数获取状态路径:
<x-dynamic-component
:component="$getFieldWrapperView()"
:field="$field"
>
<input wire:model="{{ $getStatePath() }}" />
<!-- Or -->
<input x-model="state" />
</x-dynamic-component>若组件大量依赖第三方库,建议通过 Filament 资源系统异步加载 Alpine.js 组件,这样仅在需要时加载,而非每次页面加载都引入。具体做法见 资源文档。
自定义字段类
你可以创建自己的自定义字段类与视图,在项目中复用,甚至作为插件发布给社区。
可用以下命令创建自定义字段类与视图:
php artisan make:filament-form-field LocationPicker这将创建如下组件类:
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 中另一组件的状态:
<x-dynamic-component
:component="$getFieldWrapperView()"
:field="$field"
>
{{ $get('email') }}
</x-dynamic-component>TIP
除非表单字段是响应式(reactive)的,否则字段值变化时 Blade 视图不会刷新,仅在下次引发服务器请求的用户交互时才会更新。若需对字段值变化做出反应,应使用 live()。
在 Blade 视图中访问 Eloquent 记录
在 Blade 视图中,可用 $record 变量访问当前 Eloquent 记录:
<x-dynamic-component
:component="$getFieldWrapperView()"
:field="$field"
>
{{ $record->name }}
</x-dynamic-component>在 Blade 视图中访问当前操作
在 Blade 视图中,可用 $operation 变量访问当前操作,通常为 create、edit 或 view:
<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 组件实例:
@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 访问当前字段实例。可对该对象调用公共方法,以获取变量中可能没有的其他信息:
<x-dynamic-component
:component="$getFieldWrapperView()"
:field="$field"
>
@if ($field->getState())
This is a new conference.
@endif
</x-dynamic-component>为自定义字段类添加配置方法
可在自定义字段类上添加公共方法:接受配置值、存入受保护属性,再通过另一公共方法返回:
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:
<x-dynamic-component
:component="$getFieldWrapperView()"
:field="$field"
>
{{ $getZoom() }}
</x-dynamic-component>在自定义字段类上定义的任何公共方法,都可按此方式在 Blade 视图中作为变量函数访问。
向自定义字段类传入配置值时,可使用该公共方法:
use App\Filament\Forms\Components\LocationPicker;
LocationPicker::make('location')
->zoom(0.5)在自定义字段配置方法中允许工具注入
工具注入(Utility injection) 是 Filament 的强大功能,允许用户用可访问各种工具的函数配置组件。要允许工具注入,请确保配置的参数类型与属性类型允许传入 Closure。在 getter 方法中,应将配置值传给 $this->evaluate():若用户传入函数则注入工具,若为静态值则直接返回:
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() 传入静态值或函数,并注入任意工具作为参数:
use App\Filament\Forms\Components\LocationPicker;
LocationPicker::make('location')
->zoom(fn (Conference $record): float => $record->isGlobal() ? 1 : 0.5)遵循状态绑定修饰符
将字段绑定到状态路径时,可使用 defer 修饰符,确保仅在用户提交表单或下次发起 Livewire 请求时才把状态发送到服务器。这是默认行为。
Filament 提供 $applyStateBindingModifiers() 函数,可在视图中把状态绑定修饰符应用到 wire:model 或 $wire.$entangle() 绑定:
<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] 属性:
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() 获取),第二个是方法名,第三个参数可传入实参:
@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] 属性以跳过重新渲染:
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
{
// ...
}
}