Livewire/Alpine 切换绑定的动态数量

Livewire/Alpine Dynamic # of Toggle Binding

我有一个显示多个 toggle switches 的表单。

开关数量根据 table 中的“权限”动态变化。我如何绑定这些以便在单击它们时将数据发回 livewire?我找到的 'Non-dynamic' 答案是 x-data="{isChecked: @entangle('foo')}" 但如果我有未知数量的项目而不是单个 'foo'.

我尝试了一种方法 wire:click="update({{ $value->id }})" 但这只会将被点击元素的 ID 传回 livewire,而不是它的状态(开或关)。

@foreach($permissions as $key => $value)
   <div>
      <span>
         <span>{{ $value->name }}</span>
         <span>{{ $value->description }}</span>
      </span>
      <button type="button"
         x-data="{isChecked: {{ $value->allowed ? 1 : 0 }}}"
         @click="isChecked = !isChecked"
         {# wire:click? doesn't send state #}
         wire:click="update"
         :class="{'bg-liteblue': isChecked, 'bg-gray-200': !isChecked }"
         class="relative..."
         role="switch"
         :aria-checked="isChecked"
         aria-labelledby="availability-label">
         <span class="sr-only">Use setting</span>
         <span aria-hidden="true"
            :class="{'translate-x-5': isChecked, 'translate-x-0': !isChecked }"
            class="translate-x-5 transition ease-in-out duration-200"></span>
      </button>
   </div>
@endforeach

我希望每次切换都在单击时更新数据库(而不是在最后一次提交中)。如何将状态传回 livewire 控制器?

我认为您需要在 x 数据行中使用“@entangle”。以下是 https://laravel-livewire.com/docs/2.x/alpine-js 上大约 1/2 向下的示例。这纠缠 javascript 变量 = 打开 livewire public 属性 $showDropdown。当您更改 open 的值时,public 属性 $showDropdown 将更改为与 open 等效的值。但是,我认为通信只有一种方式——apine to livewire。

<div x-data="{ open: @entangle('showDropdown') }">

或者你可以使用这样的东西

<button x-on:click="$wire.someMethod($value->id)">xxxxxxx</button>

该参数将识别您点击了哪个开关。 livewire 方法 - someMethod() - 然后会将选择保存在数据库中。

我遇到了类似的问题。以下是我正在尝试的。当点击事件被触发时,它会切换 alpine 变量 'enableit'。 alert(enableit) 显示正在切换的状态。但是 alert('{{ $isEnabled }}') 显示为空白。但是,如果我查看控制台中返回的 json,我会看到 $isEnabled 属性 被切换,但与 enableit 变量相反。

@props([
    'choice' => 'Make Administrator?',
    'desc' => 'We encourage you to make your spouse / partner an administrator',
    'isEnabled' => false,
])
<div
    x-data="{ open: @entangle('showToggleButton').defer,
              enableit: @entangle('isEnabled')}"
    class="flex items-center justify-between"
>
  <span x-show="open" class="flex-grow flex flex-col" id="availability-label">
    <span class="text-sm font-medium text-gray-900 mr-4">{{ $choice }}</span>
    <span class="text-sm text-gray-500 mr-4">{{ $desc }}</span>
  </span>
    <!-- Enabled: "bg-indigo-600", Not Enabled: "bg-gray-200" -->
    <button x-show="open" type="button"
            @click="enableit = !enableit; alert(enableit); alert('{{ $isEnabled }}')"
            class="{{ ($isEnabled) ? 'bg-indigo-600' : 'bg-gray-200' }}  relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
            role="switch" aria-checked="enableit" aria-labelledby="availability-label">
        <span x-show="open"  class="sr-only">Use setting</span>
        <!-- Enabled: "translate-x-5", Not Enabled: "translate-x-0" -->
        <span x-show="open" aria-hidden="!enableit"
              class="{{ ($isEnabled) ? 'translate-x-5' : 'translate-x-0' }} pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200"></span>
    </button>
</div>

顺便说一句,你提到了

# wire:click? doesn't send state #

我相信你应该在 alpine 组件中使用 $wire.click。

我用一个名为 'permission-toggle' 的子组件解决了这个问题,这样每个切换都可以负责保持它的状态而不是一个数组,然后在父组件中用

实例化它
@foreach($permissions as $permission)

        <livewire:permission-toggle :group="$selectedGroup" :permission="$permission" :key="$selectedGroup.'.'.$permission->slug">

@endforeach

父“permission-form”组件负责从数据库获取所有权限(因此只有一个大的加载调用),然后每个单独的“permission-toggle”组件负责更新自身。 (个别数据库调用)权限切换的 livewire 控制器如下

class PermissionToggle extends Component
{
    public bool   $checked;
    public string $name;
    public string $description;
    public string $slug;
    public string $group;

    public function mount($permission)
    {
        $this->checked     = $permission->allowed;
        $this->name        = $permission->name;
        $this->description = $permission->description;
        $this->slug        = $permission->slug;
    }

    public function updating($a, bool $checked)
    {
        $group = Group::where('slug', $this->group)->first();

        if($checked === true)
            $group->assignPermissions($this->slug);
        else
            $group->revokePermissions($this->slug);
    }

    public function render()
    {
        return view('livewire.permission-toggle');
    }
} 

回答这个问题有几个层次。第一个是:

为什么@entangle?

如果你要通过 Livewire 同步数据,那么严格来说(至少从我从你的代码中理解的形式),你不需要将数据与 Alpine 纠缠在一起。

二级是:

如果你坚持使用@entangle

您不需要创建全新的组件。你可以纠缠一个数组(和数组的数组,以及数组的数组的数组):

毫无疑问,您接受的解决方案有效。但在我看来,它看起来像是一个非常冗长的解决方案。我不喜欢掩埋 classes/files 层来解决一个应该作为单一概念“考虑”的解决方案。当您必须翻阅多个文件时,它会使长期维护更加耗时。 (也许是个人喜好,随便吧。)

我处理这个问题的方式如下。在原始 Livewire 组件中定义单个数组:

class PropertiesPage extends Component
{
    // in the real world, this array would be populated from a DB
    public array $permissions = [
        'inventory' =>  [ // group level
            'view_inv' => [ // permission level
                'description' => 'View Inventory',
                'slug'        => '/viewinv',
                'is_checked'  => true,
            ],
            'update_inv' => [
                'description' => 'Modify Inventory',
                'slug'        => '/modinv',
                'is_checked'  => false,
            ],
        ],
    ];

在你的 blade.php 文件中,你然后纠缠整个数组:

    <div x-data="dbPermissions: @entangle('permissions')">
        <p>Blah...</p>
    </div>

然后您可以使用 Blade 的 @foreach 以类似于您在问题中所做的方式循环遍历数组。 但是,正如我在上面的第一点中强调的那样,如果您的逻辑和同步发生在 Blade/Livewire 中,为什么还要麻烦 @entangle-ing?当然,如果首先想要 @entangle 的重点是使用 Alpine 的循环解决方案,也就是说你可以这样做:

    <template x-for="(perms, group) in dbPermissions">
        <p x-text="group"></p>
        <template x-for="(props, permId) in perms">
            <input type="radio" x-model="dbPermissions[group][permId].is_checked">
        </template>
    </template>

当然,我所有的代码都是伪代码,因为我没有完全掌握您的实际解决方案。我在此站点上的意图始终是公开分享概念,而不是将您的特定问题解决到 T。