You can add the confimation dialog as document said.
您可以添加文档中所说的确认对话框。
Here's an example:
下面是一个例子:
Forms\Components\Repeater::make('test')
->schema([
Forms\Components\TextInput::make('namee')->required()
])
->deleteAction(fn(Forms\Components\Actions\Action $action) => $action->requiresConfirmation())
Updated (V2):
Since the previous solution doesn't work in version 2, Below two options are available:
更新(V2):由于以前的解决方案在版本2中不起作用,因此提供以下两个选项:
First Option:
You can override the repeater component to add a confirmation.
To do this, you need to publish filament form views using php artisan vendor:publish --provider="Filament\Forms\FormsServiceProvider"
and then customize the repeater component located in resources/views/vendors/forms/components/repeater.blade.php
and add onclick event to the trash icon.
第一个选项:您可以覆盖中继器组件以添加确认。要做到这一点,您需要使用php artisan vendor:publish --provider=“Filament\Forms\FormsServiceProvider”发布细丝表单视图,然后自定义位于resources/views/vendors/forms/components/repeater.blade.php中的repeater组件,并将onclick事件添加到垃圾桶图标。
Here's an example of final code:
下面是最终代码的一个例子:
<x-dynamic-component
:component="$getFieldWrapperView()"
:id="$getId()"
:label="$getLabel()"
:label-sr-only="$isLabelHidden()"
:helper-text="$getHelperText()"
:hint="$getHint()"
:hint-action="$getHintAction()"
:hint-color="$getHintColor()"
:hint-icon="$getHintIcon()"
:required="$isRequired()"
:state-path="$getStatePath()"
>
@php
$containers = $getChildComponentContainers();
$isCollapsible = $isCollapsible();
$isCloneable = $isCloneable();
$isReorderableWithButtons = $isReorderableWithButtons();
$isItemCreationDisabled = $isItemCreationDisabled();
$isItemDeletionDisabled = $isItemDeletionDisabled();
$isItemMovementDisabled = $isItemMovementDisabled();
$hasItemLabels = $hasItemLabels();
@endphp
<div>
@if ((count($containers) > 1) && $isCollapsible)
<div class="space-x-2 rtl:space-x-reverse" x-data="{}">
<x-forms::link
x-on:click="$dispatch('repeater-collapse', '{{ $getStatePath() }}')"
tag="button"
size="sm"
>
{{ __('forms::components.repeater.buttons.collapse_all.label') }}
</x-forms::link>
<x-forms::link
x-on:click="$dispatch('repeater-expand', '{{ $getStatePath() }}')"
tag="button"
size="sm"
>
{{ __('forms::components.repeater.buttons.expand_all.label') }}
</x-forms::link>
</div>
@endif
</div>
<div
{{
$attributes
->merge($getExtraAttributes())
->class([
'filament-forms-repeater-component space-y-6 rounded-xl',
'bg-gray-50 p-6' => $isInset(),
'dark:bg-gray-500/10' => $isInset() && config('forms.dark_mode'),
])
}}
>
@if (count($containers))
<ul>
<x-filament-support::grid
:default="$getGridColumns('default')"
:sm="$getGridColumns('sm')"
:md="$getGridColumns('md')"
:lg="$getGridColumns('lg')"
:xl="$getGridColumns('xl')"
:two-xl="$getGridColumns('2xl')"
wire:sortable
wire:end.stop="dispatchFormEvent('repeater::moveItems', '{{ $getStatePath() }}', $event.target.sortable.toArray())"
class="gap-6"
>
@foreach ($containers as $uuid => $item)
<li
x-data="{
isCollapsed: @js($isCollapsed($item)),
}"
x-on:repeater-collapse.window="$event.detail === '{{ $getStatePath() }}' && (isCollapsed = true)"
x-on:repeater-expand.window="$event.detail === '{{ $getStatePath() }}' && (isCollapsed = false)"
wire:key="{{ $this->id }}.{{ $item->getStatePath() }}.{{ $field::class }}.item"
wire:sortable.item="{{ $uuid }}"
x-on:expand-concealing-component.window="
error = $el.querySelector('[data-validation-error]')
if (! error) {
return
}
isCollapsed = false
if (document.body.querySelector('[data-validation-error]') !== error) {
return
}
setTimeout(
() =>
$el.scrollIntoView({
behavior: 'smooth',
block: 'start',
inline: 'start',
}),
200,
)
"
@class([
'filament-forms-repeater-component-item relative rounded-xl border border-gray-300 bg-white shadow-sm',
'dark:border-gray-600 dark:bg-gray-800' => config('forms.dark_mode'),
])
>
@if ((! $isItemMovementDisabled) || (! $isItemDeletionDisabled) || $isCloneable || $isCollapsible || $hasItemLabels)
<header
@if ($isCollapsible) x-on:click.stop="isCollapsed = ! isCollapsed" @endif
@class([
'flex h-10 items-center overflow-hidden rounded-t-xl border-b bg-gray-50',
'dark:border-gray-700 dark:bg-gray-800' => config('forms.dark_mode'),
'cursor-pointer' => $isCollapsible,
])
>
@unless ($isItemMovementDisabled)
<button
title="{{ __('forms::components.repeater.buttons.move_item.label') }}"
x-on:click.stop
wire:sortable.handle
wire:keydown.prevent.arrow-up="dispatchFormEvent('repeater::moveItemUp', '{{ $getStatePath() }}', '{{ $uuid }}')"
wire:keydown.prevent.arrow-down="dispatchFormEvent('repeater::moveItemDown', '{{ $getStatePath() }}', '{{ $uuid }}')"
type="button"
@class([
'flex h-10 w-10 flex-none items-center justify-center border-r text-gray-400 outline-none transition hover:text-gray-500 focus:bg-gray-500/5',
'dark:border-gray-700 dark:focus:bg-gray-600/20' => config('forms.dark_mode'),
])
>
<span class="sr-only">
{{ __('forms::components.repeater.buttons.move_item.label') }}
</span>
<x-heroicon-s-switch-vertical
class="h-4 w-4"
/>
</button>
@endunless
<p
@class([
'flex-none truncate px-4 text-xs font-medium text-gray-600',
'dark:text-gray-400' => config('forms.dark_mode'),
])
>
{{ $getItemLabel($uuid) }}
</p>
<div class="flex-1"></div>
<ul
@class([
'flex divide-x rtl:divide-x-reverse',
'dark:divide-gray-700' => config('forms.dark_mode'),
])
>
@if ($isReorderableWithButtons)
@unless ($loop->first)
<li>
<button
title="{{ __('forms::components.repeater.buttons.move_item_up.label') }}"
type="button"
wire:click.stop="dispatchFormEvent('repeater::moveItemUp', '{{ $getStatePath() }}', '{{ $uuid }}')"
wire:target="dispatchFormEvent('repeater::moveItemUp', '{{ $getStatePath() }}', '{{ $uuid }}')"
wire:loading.attr="disabled"
@class([
'flex h-10 w-10 flex-none items-center justify-center text-gray-400 outline-none transition hover:text-gray-500 focus:bg-gray-500/5',
'dark:border-gray-700 dark:focus:bg-gray-600/20' => config('forms.dark_mode'),
])
>
<span class="sr-only">
{{ __('forms::components.repeater.buttons.move_item_up.label') }}
</span>
<x-heroicon-s-chevron-up
class="h-4 w-4"
wire:loading.remove.delay
wire:target="dispatchFormEvent('repeater::moveItemUp', '{{ $getStatePath() }}', '{{ $uuid }}')"
/>
<x-filament-support::loading-indicator
class="h-4 w-4 text-primary-500"
wire:loading.delay
wire:target="dispatchFormEvent('repeater::moveItemUp', '{{ $getStatePath() }}', '{{ $uuid }}')"
x-cloak
/>
</button>
</li>
@endunless
@unless ($loop->last)
<li>
<button
title="{{ __('forms::components.repeater.buttons.move_item_down.label') }}"
type="button"
wire:click.stop="dispatchFormEvent('repeater::moveItemDown', '{{ $getStatePath() }}', '{{ $uuid }}')"
wire:target="dispatchFormEvent('repeater::moveItemDown', '{{ $getStatePath() }}', '{{ $uuid }}')"
wire:loading.attr="disabled"
@class([
'flex h-10 w-10 flex-none items-center justify-center text-gray-400 outline-none transition hover:text-gray-500 focus:bg-gray-500/5',
'dark:border-gray-700 dark:focus:bg-gray-600/20' => config('forms.dark_mode'),
])
>
<span class="sr-only">
{{ __('forms::components.repeater.buttons.move_item_down.label') }}
</span>
<x-heroicon-s-chevron-down
class="h-4 w-4"
wire:loading.remove.delay
wire:target="dispatchFormEvent('repeater::moveItemDown', '{{ $getStatePath() }}', '{{ $uuid }}')"
/>
<x-filament-support::loading-indicator
class="h-4 w-4 text-primary-500"
wire:loading.delay
wire:target="dispatchFormEvent('repeater::moveItemDown', '{{ $getStatePath() }}', '{{ $uuid }}')"
x-cloak
/>
</button>
</li>
@endunless
@endif
@if ($isCloneable)
<li>
<button
title="{{ __('forms::components.repeater.buttons.clone_item.label') }}"
wire:click.stop="dispatchFormEvent('repeater::cloneItem', '{{ $getStatePath() }}', '{{ $uuid }}')"
wire:target="dispatchFormEvent('repeater::cloneItem', '{{ $getStatePath() }}', '{{ $uuid }}')"
wire:loading.attr="disabled"
type="button"
@class([
'flex h-10 w-10 flex-none items-center justify-center text-gray-400 outline-none transition hover:text-gray-500 focus:bg-gray-500/5',
'dark:border-gray-700 dark:focus:bg-gray-600/20' => config('forms.dark_mode'),
])
>
<span class="sr-only">
{{ __('forms::components.repeater.buttons.clone_item.label') }}
</span>
<x-heroicon-s-duplicate
class="h-4 w-4"
wire:loading.remove.delay
wire:target="dispatchFormEvent('repeater::cloneItem', '{{ $getStatePath() }}', '{{ $uuid }}')"
/>
<x-filament-support::loading-indicator
class="h-4 w-4 text-primary-500"
wire:loading.delay
wire:target="dispatchFormEvent('repeater::cloneItem', '{{ $getStatePath() }}', '{{ $uuid }}')"
x-cloak
/>
</button>
</li>
@endunless
@unless ($isItemDeletionDisabled)
<li>
<button
title="{{ __('forms::components.repeater.buttons.delete_item.label') }}"
wire:click.stop="dispatchFormEvent('repeater::deleteItem', '{{ $getStatePath() }}', '{{ $uuid }}')"
wire:target="dispatchFormEvent('repeater::deleteItem', '{{ $getStatePath() }}', '{{ $uuid }}')"
wire:loading.attr="disabled"
type="button"
@class([
'flex h-10 w-10 flex-none items-center justify-center text-danger-600 outline-none transition hover:text-danger-500 focus:bg-gray-500/5',
'dark:text-danger-500 dark:hover:text-danger-400 dark:focus:bg-gray-600/20' => config('forms.dark_mode'),
])
>
<span class="sr-only">
{{ __('forms::components.repeater.buttons.delete_item.label') }}
</span>
<x-heroicon-s-trash
class="h-4 w-4"
{{-- Here we add our onclick event --}}
onclick="confirm('Are you sure you want to remove this?') || event.stopImmediatePropagation()"
wire:loading.remove.delay
wire:target="dispatchFormEvent('repeater::deleteItem', '{{ $getStatePath() }}', '{{ $uuid }}')"
/>
<x-filament-support::loading-indicator
class="h-4 w-4 text-primary-500"
wire:loading.delay
wire:target="dispatchFormEvent('repeater::deleteItem', '{{ $getStatePath() }}', '{{ $uuid }}')"
x-cloak
/>
</button>
</li>
@endunless
@if ($isCollapsible)
<li>
<button
x-bind:title="
! isCollapsed
? '{{ __('forms::components.repeater.buttons.collapse_item.label') }}'
: '{{ __('forms::components.repeater.buttons.expand_item.label') }}'
"
x-on:click.stop="isCollapsed = ! isCollapsed"
type="button"
@class([
'flex h-10 w-10 flex-none items-center justify-center text-gray-400 outline-none transition hover:text-gray-500 focus:bg-gray-500/5',
'dark:focus:bg-gray-600/20' => config('forms.dark_mode'),
])
>
<x-heroicon-s-minus-sm
class="h-4 w-4"
x-show="! isCollapsed"
/>
<span
class="sr-only"
x-show="! isCollapsed"
>
{{ __('forms::components.repeater.buttons.collapse_item.label') }}
</span>
<x-heroicon-s-plus-sm
class="h-4 w-4"
x-show="isCollapsed"
x-cloak
/>
<span
class="sr-only"
x-show="isCollapsed"
x-cloak
>
{{ __('forms::components.repeater.buttons.expand_item.label') }}
</span>
</button>
</li>
@endif
</ul>
</header>
@endif
<div
x-bind:class="{
'invisible h-0 !m-0 overflow-y-hidden': isCollapsed,
'p-6': ! isCollapsed,
}"
>
{{ $item }}
</div>
<div
class="p-2 text-center text-xs text-gray-400"
x-show="isCollapsed"
x-cloak
>
{{ __('forms::components.repeater.collapsed') }}
</div>
</li>
@endforeach
</x-filament-support::grid>
</ul>
@endif
@if (! $isItemCreationDisabled)
<div class="relative flex justify-center">
<x-forms::button
:wire:click="'dispatchFormEvent(\'repeater::createItem\', \'' . $getStatePath() . '\')'"
size="sm"
outlined
>
{{ $getCreateItemButtonLabel() }}
</x-forms::button>
</div>
@endif
</div>
</x-dynamic-component>
You can search for Here we add our onclick event
to easily find customization.
您可以在此处搜索,我们添加了onClick事件以轻松查找定制。
Second Option:
It seems you tried repeater::deleteItem
, but there's a point.
When you define a listener with the same name, it will not override for some reason, and because of that, you still have the default behavior.
第二种选择:似乎您尝试过Repeater::DeleteItem,但这是有道理的。当您使用相同的名称定义侦听器时,由于某些原因,它将不会被覆盖,因此,您仍然具有默认行为。
To override that listener, it tried something like this:
为了覆盖该监听程序,它尝试了如下内容:
First, create a new repeater component class.
首先,创建一个新的中继器组件类。
For example, let's create Repeater.php
in app/Filament/Forms/Compontent
. This class extends Filament's Repeater component class.
例如,让我们在app/Filame/Forms/Compontent中创建Repeater.php。这个类扩展了FIREMENT的中继器组件类。
<?php
namespace App\Filament\Forms\Component;
class Repeater extends \Filament\Forms\Components\Repeater
{
protected function setUp(): void
{
parent::setUp();
$this->listeners['repeater::deleteItem'] = [
function (\Filament\Forms\Components\Repeater $component, string $statePath, string $uuidToDelete): void {
// !! below lines are default behaviour of this action and you can customize them !!
if ($statePath !== $component->getStatePath()) {
return;
}
$items = $component->getState();
unset($items[$uuidToDelete]);
$livewire = $component->getLivewire();
data_set($livewire, $statePath, $items);
},
];
}
}
Then, you need to bind Filament's Repeater to your new Repeater.
然后,您需要将Filament的Repeater绑定到新的Repeater。
In AppServiceProvider
:
在AppServiceProvider中:
use App\Filament\Forms\Component\Repeater;
// ...
public function register(): void
{
// ...
$this->app->bind(\Filament\Forms\Components\Repeater::class, Repeater::class);
}
Now repeater::deleteItem
does whatever you say.
现在Repeater::DeleteItem按您说的做。
更多回答
You're right, for version 3, thank-you. I was running version 2 at the time of posting this, I have since updated the question.
你说得对,对于第三版,谢谢你。在我发布这篇文章时,我正在运行版本2,此后我更新了问题。
@charliefortune I updated the answer for version 2. There's no built-in way, and you must override some core functionality.
@CharlieFortune我更新了版本2的答案。没有内置的方法,你必须覆盖一些核心功能。
我是一名优秀的程序员,十分优秀!