gpt4 book ai didi

Add confirm delete modal to filament v2 repeater items(将确认删除模式添加到灯丝v2中继器项目)

转载 作者:bug小助手 更新时间:2023-10-25 22:14:02 25 4
gpt4 key购买 nike



I have a correctly working FilamentPHP v2 repeater. I am trying to add a 'confirm delete' modal that the user must accept before the delete action proceeds, a pretty normal use case.

我有一个工作正常的FilamentPHPv2中继器。我正在尝试添加一个用户在继续删除操作之前必须接受的‘确认删除’模式,这是一个非常正常的用例。


There doesn't seem to be anything built in to handle this, and the views are generated - so firing my own events from the delete click in the component seems like it will be tricky.

似乎没有任何内置的东西来处理这一点,而且视图是生成的-所以在组件中单击删除来激发我自己的事件似乎是很棘手的。


I have also tried using the built-in events, such as repeater:deleteItem - but this fires AFTER the delete has taken place, which is too late as it has already been removed from the interface.

我还尝试使用内置事件,如Repeater:DeleteItem--但这在删除发生后触发,但为时已晚,因为它已经从接口中删除。


Is there a standard way to deal with this that I've missed?

有没有一个标准的方法来处理我错过的这件事?


更多回答
优秀答案推荐

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的答案。没有内置的方法,你必须覆盖一些核心功能。

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com