在 Filament 中创建自定义调色板字段 - 2/3
这一部分我们将开始改变表单的状态 state。不过,先了解一些基础知识。
Filament 表单中的所有字段都有一个唯一的 ”state path"。state path 是 Livewire 组件上的位置,该组件包含可以在其中找到字段的当前值/状态的表单。
表单字段的 state path 可以使用 getStatePath()
方法检索。可以在字段类中调用或者在 Blade 视图中通过 $getStatePath
变量调用。
让我们从使颜色按钮更新字段的状态开始。我们将使用 Alpine 来实现这一点,而不是 Livewire 自己的 wire:
指令。
<x-forms::field-wrapper
:id="$getId()"
:label="$getLabel()"
:label-sr-only="$isLabelHidden()"
:helper-text="$getHelperText()"
:hint="$getHint()"
:hint-icon="$getHintIcon()"
:required="$isRequired()"
:state-path="$getStatePath()"
>
<div
x-data="{ state: $wire.entangle('{{ $getStatePath() }}') }"
class="flex items-center space-x-4"
>
@foreach($getOptions() as $color => $label)
<button
type="button"
x-on:click="state = @js($color)"
class="rounded-full w-8 h-8 border border-gray-500"
style="background: {{ $color }}" title="{{ $label }}"
>
<span class="sr-only">
{{ $label }}
</span>
</button>
@endforeach
</div>
</x-forms::field-wrapper>
在我的测试表单组件中,我添加了一个 updateColor
钩子,该钩子会在状态更新时被调用。
public function updatedColor()
{
dd($this->color);
}
这便是结果...
dd()
函数被调用,并且 state path 上的属性更新了。很好!
在 Filament 中写入最优字段很重要,因为任何不必要的状态更新都会导致不断来回的 Livewire 请求和多次重新渲染。如果我选择一个选项,将发送 1 个请求来更新状态。如果我发现这是一个错误的选项,并选择另一个,则会发送第二个请求来再次更新状态。
这些重复的请求并不总是必要的,Filament 字段通常遵循优先延迟(defer-first)的方法。如果您不熟悉 Livewire 的延迟绑定系统,请阅读文档。
Filament 中的字段负责它们自己的状态绑定方法(reactive、deferred 或 lazy)。要获得当前的绑定修饰符,我们可以使用$applyStateBindingModifiers()
函数。
这个函数接受一个字符串,这个字符串将是您想要应用修饰符的方法或指令。在我们的例子中,它将是 $wire
上的 entangle()
方法。
更新 Blade 视图中的代码如下:
<x-forms::field-wrapper
:id="$getId()"
:label="$getLabel()"
:label-sr-only="$isLabelHidden()"
:helper-text="$getHelperText()"
:hint="$getHint()"
:hint-icon="$getHintIcon()"
:required="$isRequired()"
:state-path="$getStatePath()"
>
<div x-data="{ state: $wire.{{ $applyStateBindingModifiers('entangle(\'' . $getStatePath() . '\')') }} }" class="flex items-center space-x-4">
@foreach($getOptions() as $color => $label)
<button
type="button"
x-on:click="state = @js($color)"
class="rounded-full w-8 h-8 border border-gray-500"
style="background: {{ $color }}" title="{{ $label }}"
>
<span class="sr-only">
{{ $label }}
</span>
</button>
@endforeach
</div>
</x-forms::field-wrapper>
诚然,由于字符串串联和单引号转义,这段代码一开始有点难以阅读。它所做的只是建立与以前的 entangle('{{ $getStatePath() }}')
字符串相同,并通过 $applyStateBindingModifiers()
发送它。
在一个简单的 ColorPalette::make()
调用中,这将导致 .defer
修饰符被应用于 $wire.integre()
调用,这意味着任何状态更改都将推迟到调用下一个 Livewire 操作,通常是保存表单或更改反应字段的状态。
要使字段再次响应,只需要对字段本身调用 ->reactive()
,也就是
ColorPalette::make('color')
->reactive(),
选中的选项可视化展示
从功能上讲,这个字段正在发挥作用。但从视觉上看,无法判断选择了哪个选项。让我们通过对所选选项添加一个环以及一个复选标记来改变这一点。
我们可以再次使用 Alpine 将类条件性地应用到按钮上,因为我们已经有了对当前状态的引用。
<x-forms::field-wrapper
:id="$getId()"
:label="$getLabel()"
:label-sr-only="$isLabelHidden()"
:helper-text="$getHelperText()"
:hint="$getHint()"
:hint-icon="$getHintIcon()"
:required="$isRequired()"
:state-path="$getStatePath()"
>
<div x-data="{ state: $wire.{{ $applyStateBindingModifiers('entangle(\'' . $getStatePath() . '\')') }} }" class="flex items-center space-x-4">
@foreach($getOptions() as $color => $label)
<button
type="button"
x-on:click="state = @js($color)"
class="rounded-full w-8 h-8 border border-gray-300 appearance-none inline-flex items-center justify-center"
x-bind:class="{
'ring-2 ring-gray-300 ring-offset-2': state === @js($color),
}"
style="background: {{ $color }}" title="{{ $label }}"
>
<span class="sr-only">
{{ $label }}
</span>
<span x-show="state === @js($color)" x-cloak>
<x-heroicon-o-check class="w-4 h-4 text-gray-400" />
</span>
</button>
@endforeach
</div>
</x-forms::field-wrapper>
使用 x-bind:class
和 x-show
,我们可以有条件地将类添加到按钮中,并切换来自于 Heroicon 中的选中图标。
在本系列的下一部分也就是最后一部分中,我们将研究一些可以添加到 ColorPalette
中的额外方法,使其对开发人员更友好。