编程

在 Filament 中创建自定义调色板字段 - 2/3

975 2023-06-02 22:05:00

这一部分我们将开始改变表单的状态 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:classx-show,我们可以有条件地将类添加到按钮中,并切换来自于 Heroicon 中的选中图标。

在本系列的下一部分也就是最后一部分中,我们将研究一些可以添加到 ColorPalette 中的额外方法,使其对开发人员更友好。